Hübsches Drucken von XML mit Javascript

135

Ich habe eine Zeichenfolge, die ein nicht eingerücktes XML darstellt, das ich schön drucken möchte. Beispielsweise:

<root><node/></root>

soll werden:

<root>
  <node/>
</root>

Syntaxhervorhebung ist keine Voraussetzung. Um das Problem zu lösen, transformiere ich zuerst das XML, um Zeilenumbrüche und Leerzeichen hinzuzufügen, und verwende dann ein Pre- Tag, um das XML auszugeben. Um neue Linien und Leerzeichen hinzuzufügen, habe ich die folgende Funktion geschrieben:

function formatXml(xml) {
    var formatted = '';
    var reg = /(>)(<)(\/*)/g;
    xml = xml.replace(reg, '$1\r\n$2$3');
    var pad = 0;
    jQuery.each(xml.split('\r\n'), function(index, node) {
        var indent = 0;
        if (node.match( /.+<\/\w[^>]*>$/ )) {
            indent = 0;
        } else if (node.match( /^<\/\w/ )) {
            if (pad != 0) {
                pad -= 1;
            }
        } else if (node.match( /^<\w[^>]*[^\/]>.*$/ )) {
            indent = 1;
        } else {
            indent = 0;
        }

        var padding = '';
        for (var i = 0; i < pad; i++) {
            padding += '  ';
        }

        formatted += padding + node + '\r\n';
        pad += indent;
    });

    return formatted;
}

Ich rufe dann die Funktion folgendermaßen auf:

jQuery('pre.formatted-xml').text(formatXml('<root><node1/></root>'));

Das funktioniert für mich einwandfrei, aber während ich die vorherige Funktion schrieb, dachte ich, dass es einen besseren Weg geben muss. Meine Frage ist also, ob Sie eine XML-Zeichenfolge besser kennen, um sie auf einer HTML-Seite hübsch auszudrucken. Alle Javascript-Frameworks und / oder Plugins, die diese Aufgabe übernehmen könnten, sind willkommen. Meine einzige Anforderung ist, dass dies auf der Client-Seite erfolgt.

Darin Dimitrov
quelle
2
Eine ausgefallene HTML-Ausgabe (ua IE-XML-Anzeige) finden Sie in der im XPath Visualizer verwendeten XSLT-Umwandlung. Sie können den XPath Visualizer unter folgender Adresse
Dimitre Novatchev
/.+<\/\w[^>‹*>$/ - Entfernen Sie "+" in diesem RegExp, da dies den Code in einigen JavaScript-Engines für Knoten mit "langen Attributwerten" verlangsamt.
4esn0k

Antworten:

58

Aus dem Text der Frage habe ich den Eindruck, dass ein Zeichenfolgenergebnis erwartet wird , im Gegensatz zu einem HTML-formatierten Ergebnis.

In diesem Fall besteht der einfachste Weg, dies zu erreichen, darin, das XML-Dokument mit der Identitätstransformation und einer <xsl:output indent="yes"/>Anweisung zu verarbeiten :

<xsl: stylesheet version = "1.0"
 xmlns: xsl = "http://www.w3.org/1999/XSL/Transform">
 <xsl: Ausgabe omit-xml-declare = "yes" indent = "yes" />

    <xsl: template match = "node () | @ *">
      <xsl: copy>
        <xsl: apply-templates select = "node () | @ *" />
      </ xsl: copy>
    </ xsl: template>
</ xsl: Stylesheet>

Wenn Sie diese Umwandlung auf das bereitgestellte XML-Dokument anwenden:

<root> <node/> </ root>

Die meisten XSLT-Prozessoren (.NET XslCompiledTransform, Saxon 6.5.4 und Saxon 9.0.0.2, AltovaXML) liefern das gewünschte Ergebnis:

<wurzel>
  <Knoten />
</ root>
Dimitre Novatchev
quelle
3
Es sieht nach einer großartigen Lösung aus. Gibt es eine browserübergreifende Möglichkeit, diese Umwandlung in Javascript anzuwenden? Ich habe kein serverseitiges Skript, auf das ich mich verlassen kann.
Darin Dimitrov
2
Ja. Schauen Sie sich Sarissa an: dev.abiss.gr/sarissa und hier: xml.com/pub/a/2005/02/23/sarissa.html
Dimitre Novatchev
6
@ablmf: Was "funktioniert nicht"? Was ist "Chrome"? Ich habe noch nie von einem solchen XSLT-Prozessor gehört. Wenn Sie sich das Datum der Antwort ansehen, war der Chrome-Browser zu diesem Zeitpunkt noch nicht vorhanden.
Dimitre Novatchev
3
@ablmf: Beachten Sie auch, dass diese Frage (und meine Antwort darauf) darin besteht, das hübsche XML als Zeichenfolge (Text) und nicht als HTML zu erhalten. Kein Wunder, dass eine solche Zeichenfolge in einem Browser nicht angezeigt wird. Eine ausgefallene HTML-Ausgabe (ua IE-XML-Anzeige) finden Sie in der im XPath Visualizer verwendeten XSLT-Umwandlung. Sie können den XPath Visualizer unter folgender Adresse herunterladen: huttar.net/dimitre/XPV/TopXML-XPV.html . Möglicherweise müssen Sie den Code ein wenig anpassen (z. B. um die Javascript-Erweiterungsfunktionen zum Reduzieren / Erweitern eines Knotens zu entfernen), andernfalls sollte der resultierende HTML-Code einwandfrei angezeigt werden.
Dimitre Novatchev
2
JohnK, 2008, als diese Frage beantwortet wurde, initiierten die Leute XSLT-Transformationen aus JavaScript im IE und riefen MSXML3 auf. Jetzt können sie dies noch tun, obwohl der mit IE11 gelieferte XSLT-Prozessor MSXML6 ist. Alle anderen Browser verfügen über ähnliche Funktionen, obwohl sie über unterschiedliche integrierte XSLT-Prozessoren verfügen. Aus diesem Grund hat der ursprüngliche Fragesteller eine solche Frage nie gestellt.
Dimitre Novatchev
32

Leichte Änderung der Javascript-Funktion von efnx clckclcks. Ich habe die Formatierung von Leerzeichen in Tabulatoren geändert, aber vor allem habe ich zugelassen, dass Text in einer Zeile bleibt:

var formatXml = this.formatXml = function (xml) {
        var reg = /(>)\s*(<)(\/*)/g; // updated Mar 30, 2015
        var wsexp = / *(.*) +\n/g;
        var contexp = /(<.+>)(.+\n)/g;
        xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
        var pad = 0;
        var formatted = '';
        var lines = xml.split('\n');
        var indent = 0;
        var lastType = 'other';
        // 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions 
        var transitions = {
            'single->single': 0,
            'single->closing': -1,
            'single->opening': 0,
            'single->other': 0,
            'closing->single': 0,
            'closing->closing': -1,
            'closing->opening': 0,
            'closing->other': 0,
            'opening->single': 1,
            'opening->closing': 0,
            'opening->opening': 1,
            'opening->other': 1,
            'other->single': 0,
            'other->closing': -1,
            'other->opening': 0,
            'other->other': 0
        };

        for (var i = 0; i < lines.length; i++) {
            var ln = lines[i];

            // Luca Viggiani 2017-07-03: handle optional <?xml ... ?> declaration
            if (ln.match(/\s*<\?xml/)) {
                formatted += ln + "\n";
                continue;
            }
            // ---

            var single = Boolean(ln.match(/<.+\/>/)); // is this line a single tag? ex. <br />
            var closing = Boolean(ln.match(/<\/.+>/)); // is this a closing tag? ex. </a>
            var opening = Boolean(ln.match(/<[^!].*>/)); // is this even a tag (that's not <!something>)
            var type = single ? 'single' : closing ? 'closing' : opening ? 'opening' : 'other';
            var fromTo = lastType + '->' + type;
            lastType = type;
            var padding = '';

            indent += transitions[fromTo];
            for (var j = 0; j < indent; j++) {
                padding += '\t';
            }
            if (fromTo == 'opening->closing')
                formatted = formatted.substr(0, formatted.length - 1) + ln + '\n'; // substr removes line break (\n) from prev loop
            else
                formatted += padding + ln + '\n';
        }

        return formatted;
    };
Dan BROOKS
quelle
Könnten Sie bitte Ihre Funktion aktualisieren, um den Kommentar von Chuan Ma unten zu berücksichtigen? Hat für mich gearbeitet. Vielen Dank. Edit: Ich habe es einfach selbst gemacht.
Louis LC
1
Hallo, ich habe Ihre Funktion ein wenig verbessert, um die optionale <?xml ... ?>Deklaration am Anfang des XML-Textes korrekt zu behandeln
lviggiani
31

Dies kann mit nativen Javascript-Tools ohne Bibliotheken von Drittanbietern erfolgen, wodurch die Antwort von @Dimitre Novatchev erweitert wird:

var prettifyXml = function(sourceXml)
{
    var xmlDoc = new DOMParser().parseFromString(sourceXml, 'application/xml');
    var xsltDoc = new DOMParser().parseFromString([
        // describes how we want to modify the XML - indent everything
        '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
        '  <xsl:strip-space elements="*"/>',
        '  <xsl:template match="para[content-style][not(text())]">', // change to just text() to strip space in text nodes
        '    <xsl:value-of select="normalize-space(.)"/>',
        '  </xsl:template>',
        '  <xsl:template match="node()|@*">',
        '    <xsl:copy><xsl:apply-templates select="node()|@*"/></xsl:copy>',
        '  </xsl:template>',
        '  <xsl:output indent="yes"/>',
        '</xsl:stylesheet>',
    ].join('\n'), 'application/xml');

    var xsltProcessor = new XSLTProcessor();    
    xsltProcessor.importStylesheet(xsltDoc);
    var resultDoc = xsltProcessor.transformToDocument(xmlDoc);
    var resultXml = new XMLSerializer().serializeToString(resultDoc);
    return resultXml;
};

console.log(prettifyXml('<root><node/></root>'));

Ausgänge:

<root>
  <node/>
</root>

JSFiddle

Beachten Sie, wie von @ jat255 hervorgehoben, dass hübsches Drucken mit <xsl:output indent="yes"/>von Firefox nicht unterstützt wird. Es scheint nur in Chrome, Opera und wahrscheinlich den übrigen Webkit-basierten Browsern zu funktionieren.

Klesun
quelle
Sehr schöne Antwort, aber leider verwöhnt Internet Explorer die Party wieder.
Waruyama
schön, es funktioniert nur, wenn die Eingabe von XML eine einzelne Zeile ist ... Wenn Sie sich nicht für mehrere Zeilen in Textknoten interessieren, rufen Sieprivate makeSingleLine(txt: string): string { let s = txt.trim().replace(new RegExp("\r", "g"), "\n"); let angles = ["<", ">"]; let empty = [" ", "\t", "\n"]; while (s.includes(" <") || s.includes("\t<") || s.includes("\n<") || s.includes("> ") || s.includes(">\t") || s.includes(">/n")) { angles.forEach(an => { empty.forEach(em => { s = s.replace(new RegExp(em + an, "g"), an); }); }); } return s.replace(new RegExp("\n", "g"), " "); }
Sasha Bond
5
Ich erhalte eine Fehlermeldung, aber der Fehler hat keine Meldung. Es passiert auch in der Geige mit Firefox.
Tomáš Zato - Wiedereinstellung Monica
Dies funktioniert auch nicht für mich mit einem leeren Fehler in Firefox
jat255
1
Dies wird unter folgender Adresse erläutert: stackoverflow.com/questions/51989864/… Anscheinend benötigt Firefox eine Versionsspezifikation für xsl, aber es spielt sowieso keine Rolle, da die Mozilla-Implementierung kein xsl:outputTag berücksichtigt , sodass Sie nicht das Schöne bekommen Formatierung trotzdem.
jat255
19

Persönlich verwende ich Google-Code-Prettify mit dieser Funktion:

prettyPrintOne('<root><node1><root>', 'xml')
Touv
quelle
3
Oups, Sie müssen XML einrücken und google-code-prettify nur den Code kolorieren. Es tut uns leid.
Touv
1
kombinieren prettify mit smth wie stackoverflow.com/questions/139076/…
Chris
3
Dies zusammen mit code.google.com/p/vkbeautify für die Einrückung ergab eine gute Kombination.
Vdex
Von Google Code zu Github verschoben. Neuer Link: github.com/google/code-prettify
mUser1990
18

Ich habe diesen Thread gefunden, als ich eine ähnliche Anforderung hatte, aber den OP-Code wie folgt vereinfacht habe:

function formatXml(xml, tab) { // tab = optional indent value, default is tab (\t)
    var formatted = '', indent= '';
    tab = tab || '\t';
    xml.split(/>\s*</).forEach(function(node) {
        if (node.match( /^\/\w/ )) indent = indent.substring(tab.length); // decrease indent by one 'tab'
        formatted += indent + '<' + node + '>\r\n';
        if (node.match( /^<?\w[^>]*[^\/]$/ )) indent += tab;              // increase indent
    });
    return formatted.substring(1, formatted.length-3);
}

funktioniert bei mir!

Arcturus
quelle
Die beste Antwort !!
Jcc.Sanabria
8

Oder wenn Sie nur eine andere js-Funktion möchten, habe ich Darins (viel) geändert:

var formatXml = this.formatXml = function (xml) {
    var reg = /(>)(<)(\/*)/g;
    var wsexp = / *(.*) +\n/g;
    var contexp = /(<.+>)(.+\n)/g;
    xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
    var pad = 0;
    var formatted = '';
    var lines = xml.split('\n');
    var indent = 0;
    var lastType = 'other';
    // 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions 
    var transitions = {
        'single->single'    : 0,
        'single->closing'   : -1,
        'single->opening'   : 0,
        'single->other'     : 0,
        'closing->single'   : 0,
        'closing->closing'  : -1,
        'closing->opening'  : 0,
        'closing->other'    : 0,
        'opening->single'   : 1,
        'opening->closing'  : 0, 
        'opening->opening'  : 1,
        'opening->other'    : 1,
        'other->single'     : 0,
        'other->closing'    : -1,
        'other->opening'    : 0,
        'other->other'      : 0
    };

    for (var i=0; i < lines.length; i++) {
        var ln = lines[i];
        var single = Boolean(ln.match(/<.+\/>/)); // is this line a single tag? ex. <br />
        var closing = Boolean(ln.match(/<\/.+>/)); // is this a closing tag? ex. </a>
        var opening = Boolean(ln.match(/<[^!].*>/)); // is this even a tag (that's not <!something>)
        var type = single ? 'single' : closing ? 'closing' : opening ? 'opening' : 'other';
        var fromTo = lastType + '->' + type;
        lastType = type;
        var padding = '';

        indent += transitions[fromTo];
        for (var j = 0; j < indent; j++) {
            padding += '    ';
        }

        formatted += padding + ln + '\n';
    }

    return formatted;
};
schellsan
quelle
6

Alle hier angegebenen Javascript-Funktionen funktionieren nicht für ein XML-Dokument mit nicht angegebenen Leerzeichen zwischen dem End-Tag '>' und dem Start-Tag '<'. Um sie zu beheben, müssen Sie nur die erste Zeile in den Funktionen ersetzen

var reg = /(>)(<)(\/*)/g;

durch

var reg = /(>)\s*(<)(\/*)/g;
Chuan Ma
quelle
4

Wie wäre es, wenn Sie einen Stub-Knoten erstellen (document.createElement ('div') - oder Ihr Bibliotheksäquivalent verwenden), ihn mit der XML-Zeichenfolge (über innerHTML) füllen und eine einfache rekursive Funktion für das Stammelement / oder das Stub-Element aufrufen, falls Sie dies tun Ich habe keine Wurzel. Die Funktion würde sich für alle untergeordneten Knoten aufrufen.

Sie könnten dann auf dem Weg die Syntax hervorheben, sicherstellen, dass das Markup gut geformt ist (automatisch vom Browser beim Anhängen über innerHTML ausgeführt) usw. Es wäre nicht so viel Code und wahrscheinlich schnell genug.

Aprilkind
quelle
1
Klingt nach dem Entwurf für eine erstaunliche, elegante Lösung. Wie wäre es mit einer Implementierung?
JohnK
2
var formatXml = this.formatXml = function (xml) {
        var reg = /(>)(<)(\/*)/g;
        var wsexp = / *(.*) +\n/g;
        var contexp = /(<.+>)(.+\n)/g;
        xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
        var pad = 0;
        var formatted = '';
        var lines = xml.split('\n');
        var indent = 0;
        var lastType = 'other';
Sanjaykumar
quelle
Nachdem ich mit dieser schlecht formulierten Antwort zu kämpfen hatte, brachte ich sie zum Laufen, nehme ich an - die Ergebnisse sind nicht sehr hübsch: keine Einrückung.
JohnK
2
Or just print out the special HTML characters?

Ex: <xmlstuff>&#10; &#09;<node />&#10;</xmlstuff>   


&#09;   Horizontal tab  
&#10;   Line feed
Tobias
quelle
2

XMLSpectrum formatiert XML, unterstützt das Einrücken von Attributen und hebt die Syntax für XML und alle eingebetteten XPath-Ausdrücke hervor:

XMLSpectrum formatiertes XML

XMLSpectrum ist ein Open-Source-Projekt, das in XSLT 2.0 codiert ist. Sie können diese Server-Seite also mit einem Prozessor wie Saxon-HE (empfohlen) oder Client-Seite mit Saxon-CE ausführen.

XMLSpectrum ist noch nicht für die Ausführung im Browser optimiert - daher die Empfehlung, diese serverseitig auszuführen.

pgfearo
quelle
2

Verwenden Sie die obige Methode für hübschen Druck und fügen Sie diese dann mit der Methode jquery text () in ein beliebiges div ein . Zum Beispiel wird xmldivdann die ID von div verwendet:

$("#xmldiv").text(formatXml(youXmlString));

Sanjeev Rathaur
quelle
2
Welche "obige Methode für hübschen Druck"?
JW Lim
2

Hier ist eine weitere Funktion zum Formatieren von XML

function formatXml(xml){
    var out = "";
    var tab = "    ";
    var indent = 0;
    var inClosingTag=false;
    var dent=function(no){
        out += "\n";
        for(var i=0; i < no; i++)
            out+=tab;
    }


    for (var i=0; i < xml.length; i++) {
        var c = xml.charAt(i);
        if(c=='<'){
            // handle </
            if(xml.charAt(i+1) == '/'){
                inClosingTag = true;
                dent(--indent);
            }
            out+=c;
        }else if(c=='>'){
            out+=c;
            // handle />
            if(xml.charAt(i-1) == '/'){
                out+="\n";
                //dent(--indent)
            }else{
              if(!inClosingTag)
                dent(++indent);
              else{
                out+="\n";
                inClosingTag=false;
              }
            }
        }else{
          out+=c;
        }
    }
    return out;
}
Michael Hancock
quelle
2

Sie können hübsch formatierte XML mit XML-Verschönerung erhalten

var prettyXmlText = new XmlBeautify().beautify(xmlText, 
                    {indent: "  ",useSelfClosingElement: true});

Einzug : Einzugmuster wie Leerzeichen

useSelfClosingElement : true => Verwenden Sie ein selbstschließendes Element, wenn das Element leer ist.

JSFiddle

Original (vorher)

<?xml version="1.0" encoding="utf-8"?><example version="2.0">
  <head><title>Original aTitle</title></head>
  <body info="none" ></body>
</example>

Verschönert (nach)

<?xml version="1.0" encoding="utf-8"?>
<example version="2.0">
  <head>
    <title>Original aTitle</title>
  </head>
  <body info="none" />
</example>
Flusssonne
quelle
1
var reg = /(>)\s*(<)(\/*)/g;
xml = xml.replace(/\r|\n/g, ''); //deleting already existing whitespaces
xml = xml.replace(reg, '$1\r\n$2$3');
Jason Im
quelle
-1

Xml-to-json- Bibliothek hat Methode formatXml(xml).Ich bin der Betreuer des Projekts.

var prettyXml = formatXml("<a><b/></a>");

// <a>
//   <b/>
// </a>
Valentyn Kolesnikov
quelle