Ich kann JSON mit JsontoHtml library in HTML konvertieren. Jetzt muss ich HTML in JSON konvertieren, wie auf dieser Site gezeigt. Beim Durchschauen des Codes habe ich folgendes Skript gefunden:
<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>
Nun brauche ich die folgende Funktion in PHP. Ich kann die HTML-Daten erhalten. Jetzt brauche ich nur noch die JavaScript-Funktion in die Funktion PHP zu konvertieren. Ist das möglich? Meine größten Zweifel sind folgende:
Die primäre Eingabe für die Javascript-Funktion toTransform()
ist ein Objekt. Ist es möglich, HTML über PHP in Objekte zu konvertieren?
Sind alle Funktionen in diesem bestimmten JavaScript in PHP verfügbar?
Bitte schlagen Sie mir die Idee vor.
Wenn ich versuchte, das Skript-Tag gemäß der angegebenen Antwort in Json zu konvertieren, erhalte ich Fehler. Als ich es in der json2html-Site ausprobierte, zeigte es sich so: .. Wie erreicht man die gleiche Lösung?
Wenn Sie ein DOMDocument
-Objekt erhalten können, das Ihr HTML darstellt, müssen Sie es nur rekursiv durchlaufen und die gewünschte Datenstruktur erstellen.
Das Konvertieren Ihres HTML-Dokuments in eine DOMDocument
sollte so einfach sein:
function html_to_obj($html) {
$dom = new DOMDocument();
$dom->loadHTML($html);
return element_to_obj($dom->documentElement);
}
Dann könnte eine einfache Durchquerung von $dom->documentElement
, die die von Ihnen beschriebene Struktur angibt, folgendermaßen aussehen:
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;
}
Testfall
$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);
Ausgabe
{
"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 "
}
]
}
]
}
Antwort auf die aktualisierte Frage
Die oben vorgeschlagene Lösung funktioniert nicht mit dem <script>
-Element, da sie nicht als DOMText
, sondern als DOMCharacterData
-Objekt analysiert wird. Dies liegt daran, dass die DOM-Erweiterung in PHP auf libxml2
, basiert, das Ihren HTML-Code als HTML 4.0 analysiert. In HTML 4.0 hat der Inhalt von <script>
den Typ CDATA
und nicht #PCDATA
.
Sie haben zwei Lösungen für dieses Problem.
Die einfache, aber nicht sehr robuste Lösung wäre das Hinzufügen des LIBXML_NOCDATA
-Flags zu DOMDocument::loadHTML
. (Ich bin nicht wirklich zu 100% sicher, ob dies für den HTML-Parser funktioniert.)
Die schwierigere, aber meiner Meinung nach bessere Lösung, ist das Hinzufügen eines zusätzlichen Tests, wenn Sie $subElement->nodeType
vor der Rekursion testen. Die rekursive Funktion würde zu:
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;
}
Wenn Sie auf einen anderen Fehler dieses Typs stoßen, sollten Sie zuerst den Typ des Knotens $subElement
überprüfen, da viele andere Möglichkeiten vorhanden ist, mit dem sich meine kurze Beispielfunktion nicht befasste.
Außerdem werden Sie feststellen, dass libxml2
Fehler in Ihrem HTML-Code beheben muss, um ein DOM dafür erstellen zu können. Deshalb werden ein <html>
- und ein <head>
-Element angezeigt, auch wenn Sie sie nicht angeben. Sie können dies vermeiden, indem Sie das Flag LIBXML_HTML_NOIMPLIED
verwenden.
Testfall mit Skript
$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);
Ausgabe
{
"tag": "html",
"children": [
{
"tag": "head",
"children": [
{
"tag": "script",
"type": "text\/javascript",
"html": "\n alert('hi');\n "
}
]
}
]
}
Ich gehe davon aus, dass Ihre HTML-Zeichenfolge in der Variable $html
gespeichert ist. Also solltest du tun:
$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);
Hinweis : Dieser Algorithmus unterstützt keine Parent-Child-Tags und ruft alle Tags als übergeordnete Elemente ab und analysiert sie alle in einer sortierten Warteschlange. Natürlich können Sie diese Funktion implementieren, indem Sie die DOMDocument
-Klassenfunktionen untersuchen.