web-dev-qa-db-fra.com

Comment convertir HTML en JSON avec PHP?

Je peux convertir JSON en HTML en utilisant JsontoHtml library. Maintenant, je dois convertir le code HTML actuel en JSON, comme indiqué sur ce site. En examinant le code, j'ai trouvé le script suivant:

<script>
$(function(){

    //HTML to JSON
    $('#btn-render-json').click(function() {

        //Set html output
        $('#html-output').html( $('#html-input').val() );

        //Process to JSON and format it for consumption
        $('#html-json').html( FormatJSON(toTransform($('#html-output').children())) );
    });

});

//Convert obj or array to transform
function toTransform(obj) {

    var json;

    if( obj.length > 1 )
    {
        json = [];

        for(var i = 0; i < obj.length; i++)
            json[json.length++] = ObjToTransform(obj[i]);
    } else
        json = ObjToTransform(obj);

    return(json);
}

//Convert obj to transform
function ObjToTransform(obj)
{
    //Get the DOM element
    var el = $(obj).get(0);

    //Add the tag element
    var json = {'tag':el.nodeName.toLowerCase()};

    for (var attr, i=0, attrs=el.attributes, l=attrs.length; i<l; i++){
        attr = attrs[i];
        json[attr.nodeName] = attr.value;
    }

    var children = $(obj).children();

    if( children.length > 0 ) json['children'] = [];
    else json['html'] = $(obj).text();

    //Add the children
    for(var c = 0; c < children.length; c++)
        json['children'][json['children'].length++] = toTransform(children[c]);

    return(json);
}

//Format JSON (with indents)
function FormatJSON(oData, sIndent) {
    if (arguments.length < 2) {
        var sIndent = "";
    }
    var sIndentStyle = "  ";
    var sDataType = RealTypeOf(oData);

    // open object
    if (sDataType == "array") {
        if (oData.length == 0) {
            return "[]";
        }
        var sHTML = "[";
    } else {
        var iCount = 0;
        $.each(oData, function() {
            iCount++;
            return;
        });
        if (iCount == 0) { // object is empty
            return "{}";
        }
        var sHTML = "{";
    }

    // loop through items
    var iCount = 0;
    $.each(oData, function(sKey, vValue) {
        if (iCount > 0) {
            sHTML += ",";
        }
        if (sDataType == "array") {
            sHTML += ("\n" + sIndent + sIndentStyle);
        } else {
            sHTML += ("\"" + sKey + "\"" + ":");
        }

        // display relevant data type
        switch (RealTypeOf(vValue)) {
            case "array":
            case "object":
                sHTML += FormatJSON(vValue, (sIndent + sIndentStyle));
                break;
            case "boolean":
            case "number":
                sHTML += vValue.toString();
                break;
            case "null":
                sHTML += "null";
                break;
            case "string":
                sHTML += ("\"" + vValue + "\"");
                break;
            default:
                sHTML += ("TYPEOF: " + typeof(vValue));
        }

        // loop
        iCount++;
    });

    // close object
    if (sDataType == "array") {
        sHTML += ("\n" + sIndent + "]");
    } else {
        sHTML += ("}");
    }

    // return
    return sHTML;
}

//Get the type of the obj (can replace by jquery type)
function RealTypeOf(v) {
  if (typeof(v) == "object") {
    if (v === null) return "null";
    if (v.constructor == (new Array).constructor) return "array";
    if (v.constructor == (new Date).constructor) return "date";
    if (v.constructor == (new RegExp).constructor) return "regex";
    return "object";
  }
  return typeof(v);
}
</script>

enter image description here

Maintenant, j'ai besoin d'utiliser la fonction suivante en PHP. Je peux obtenir les données HTML. Tout ce dont j'avais besoin maintenant, c'est de convertir la fonction JavaScript en fonction PHP. Est-ce possible? Mes principaux doutes sont les suivants:

  • L'entrée principale de la fonction Javascript toTransform() est un objet. Est-il possible de convertir HTML en objet via PHP?

  • Toutes les fonctions présentes dans ce JavaScript sont-elles disponibles en PHP? 

S'il vous plaît me suggérer l'idée.

Lorsque j'ai essayé de convertir une balise de script en json selon la réponse donnée, des erreurs se sont produites. Lorsque je l'ai essayé sur le site json2html, il s'est affiché comme suit:enter image description here .. Comment réaliser la même solution?

16
Ganesh Babu

Si vous parvenez à obtenir un objet DOMDocument représentant votre code HTML, il vous suffit de le parcourir de manière récursive et de construire la structure de données souhaitée.

La conversion de votre document HTML en DOMDocument devrait être aussi simple que cela:

function html_to_obj($html) {
    $dom = new DOMDocument();
    $dom->loadHTML($html);
    return element_to_obj($dom->documentElement);
}

Ensuite, une simple traversée de $dom->documentElement qui donne le type de structure que vous avez décrit pourrait ressembler à ceci:

function element_to_obj($element) {
    $obj = array( "tag" => $element->tagName );
    foreach ($element->attributes as $attribute) {
        $obj[$attribute->name] = $attribute->value;
    }
    foreach ($element->childNodes as $subElement) {
        if ($subElement->nodeType == XML_TEXT_NODE) {
            $obj["html"] = $subElement->wholeText;
        }
        else {
            $obj["children"][] = element_to_obj($subElement);
        }
    }
    return $obj;
}

Cas de test

$html = <<<EOF
<!DOCTYPE html>
<html lang="en">
    <head>
        <title> This is a test </title>
    </head>
    <body>
        <h1> Is this working? </h1>  
        <ul>
            <li> Yes </li>
            <li> No </li>
        </ul>
    </body>
</html>

EOF;

header("Content-Type: text/plain");
echo json_encode(html_to_obj($html), JSON_PRETTY_PRINT);

Sortie

{
    "tag": "html",
    "lang": "en",
    "children": [
        {
            "tag": "head",
            "children": [
                {
                    "tag": "title",
                    "html": " This is a test "
                }
            ]
        },
        {
            "tag": "body",
            "html": "  \n        ",
            "children": [
                {
                    "tag": "h1",
                    "html": " Is this working? "
                },
                {
                    "tag": "ul",
                    "children": [
                        {
                            "tag": "li",
                            "html": " Yes "
                        },
                        {
                            "tag": "li",
                            "html": " No "
                        }
                    ],
                    "html": "\n        "
                }
            ]
        }
    ]
}

Réponse à la question mise à jour

La solution proposée ci-dessus ne fonctionne pas avec l'élément <script> car il est analysé non pas comme un DOMText, mais comme un objet DOMCharacterData. En effet, l'extension DOM dans PHP est basée sur libxml2, qui analyse votre code HTML au format HTML 4.0 et, dans HTML 4.0, le contenu de <script> est de type CDATA et non #PCDATA.

Vous avez deux solutions à ce problème.

  1. La solution simple mais peu robuste consisterait à ajouter le drapeau LIBXML_NOCDATA à DOMDocument::loadHTML. (Je ne suis pas sûr à 100% que cela fonctionne pour l'analyseur HTML.)

  2. La solution la plus difficile, mais la meilleure à mon avis, consiste à ajouter un test supplémentaire lorsque vous testez $subElement->nodeType avant la récursion. La fonction récursive deviendrait:

function element_to_obj($element) {
    echo $element->tagName, "\n";
    $obj = array( "tag" => $element->tagName );
    foreach ($element->attributes as $attribute) {
        $obj[$attribute->name] = $attribute->value;
    }
    foreach ($element->childNodes as $subElement) {
        if ($subElement->nodeType == XML_TEXT_NODE) {
            $obj["html"] = $subElement->wholeText;
        }
        elseif ($subElement->nodeType == XML_CDATA_SECTION_NODE) {
            $obj["html"] = $subElement->data;
        }
        else {
            $obj["children"][] = element_to_obj($subElement);
        }
    }
    return $obj;
}

Si vous rencontrez un autre bogue de ce type, la première chose à faire est de vérifier que le type de nœud $subElement est, car il existe de nombreuses autres possibilités mon exemple de fonction n’a pas été traité.

De plus, vous remarquerez que libxml2 doit corriger les erreurs dans votre code HTML afin de pouvoir créer un DOM pour celui-ci. C'est pourquoi les éléments <html> et <head> apparaîtront même si vous ne les spécifiez pas. Vous pouvez éviter cela en utilisant l'indicateur LIBXML_HTML_NOIMPLIED.

Cas de test avec script

$html = <<<EOF
        <script type="text/javascript">
            alert('hi');
        </script>
EOF;

header("Content-Type: text/plain");
echo json_encode(html_to_obj($html), JSON_PRETTY_PRINT);

Sortie

{
    "tag": "html",
    "children": [
        {
            "tag": "head",
            "children": [
                {
                    "tag": "script",
                    "type": "text\/javascript",
                    "html": "\n            alert('hi');\n        "
                }
            ]
        }
    ]
}
22
scozy

Je suppose que votre chaîne html est stockée dans la variable $html. Alors tu devrais faire:

$dom = new DOMDocument();
$dom->loadHTML($html);

foreach($dom->getElementsByTagName('*') as $el){
    $result[] = ["type" => $el->tagName, "value" => $el->nodeValue];
}

$json = json_encode($result, JSON_UNESCAPED_UNICODE);

Remarque : Cet algorithme ne prend pas en charge les balises parent-enfant. Il ne récupère toutes les balises en tant qu'éléments parents et les analyse dans une file d'attente triée. Bien entendu, vous pouvez implémenter cette fonctionnalité en étudiant les fonctionnalités des classes DOMDocument.

1
Amir Forsati