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>
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: .. Comment réaliser la même solution?
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.
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.)
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 "
}
]
}
]
}
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
.