web-dev-qa-db-fra.com

Jolie impression XML avec javascript

J'ai une chaîne qui représente un XML non mis en retrait que je voudrais bien imprimer. Par exemple:

<root><node/></root>

devrait devenir:

<root>
  <node/>
</root>

La mise en évidence de la syntaxe n'est pas une obligation. Pour résoudre ce problème, je transforme d'abord le code XML pour ajouter des retours à la ligne et des espaces, puis j'utilise une balise pre pour afficher le XML. Pour ajouter de nouvelles lignes et des espaces, j'ai écrit la fonction suivante:

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;
}

J'appelle alors la fonction comme ceci:

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

Cela fonctionne parfaitement pour moi, mais pendant que j'écrivais la fonction précédente, j'ai pensé qu'il devait y avoir un meilleur moyen. Ma question est donc la suivante: connaissez-vous une meilleure façon, étant donné qu’une chaîne XML soit belle d’imprimer dans une page html? Tous les frameworks et/ou plugins javascript qui pourraient faire le travail sont les bienvenus. Ma seule exigence est que cela soit fait du côté client.

120
Darin Dimitrov

À partir du texte de la question , j'ai l'impression qu'un résultat sous forme de chaîne est attendu , par opposition à un résultat au format HTML.

Si tel est le cas, le moyen le plus simple d'y parvenir est de traiter le document XML avec le transformation d'identité et avec un <xsl:output indent="yes"/> instruction :

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

 <xsl: template match = "node () | @ *"> 
 <xsl: copy > 
 <xsl: apply-templates select = "noeud () | @ *" />[____.] </ xsl: copie> 
 </ xsl: modèle> 
 </ xsl: stylesheet> 

Lors de l'application de cette transformation sur le document XML fourni:

 <root> <noeud /> </ root> 

la plupart des processeurs XSLT (.NET XslCompiledTransform, Saxon 6.5.4 et Saxon 9.0.0.2, AltovaXML) produisent le résultat souhaité:

 <root> 
 <node />
</root>
55
Dimitre Novatchev

envisager d'utiliser le plugin vkBeautify

http://www.eslinstructor.net/vkbeautify/

il est écrit en javascript, très petit: moins de 1,5K si minifié, très rapide: moins de 5 ms. traiter du texte XML 50K.

62
vadimk

Légère modification de la fonction javascript de efnx clckclcks. J'ai changé la mise en forme d'espaces en tabulation, mais j'ai surtout permis au texte de rester sur une seule ligne:

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;
    };
31
Dan BROOKS

Cela peut être fait en utilisant des outils javascript natifs, sans bibliothèque tierce, en étendant la réponse de @Dimitre Novatchev:

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>'));

Les sorties:

<root>
  <node/>
</root>

JSFiddle

22
Artur Klesun

Personnellement, j'utilise google-code-prettify avec cette fonction:

prettyPrintOne('<root><node1><root>', 'xml')
19
Touv

J'ai trouvé ce fil lorsque j'avais une exigence similaire, mais j'ai simplifié le code de OP comme suit:

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);
}

travaille pour moi!

12
arcturus

Ou si vous souhaitez simplement qu'une autre fonction js le fasse, j'ai modifié Darin (beaucoup):

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;
};
8
schellsan

Cette bibliothèque fait exactement ce que vous voulez!

https://code.google.com/p/vkbeautify/

7
Shivanshu Goyal

Toutes les fonctions javascript indiquées ici ne fonctionneront pas pour un document xml comportant des espaces non spécifiés entre la balise de fin ">" et la balise de début "<". Pour les corriger, il vous suffit de remplacer la première ligne dans les fonctions

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

par

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

que diriez-vous de créer un nœud de stub (document.createElement ('div') - ou d’utiliser votre équivalent de bibliothèque), de le remplir avec la chaîne xml (via innerHTML) et d’appeler une fonction récursive simple pour l’élément racine/ou l’élément stub au cas où vous n'a pas de racine. La fonction s’appellera pour tous les nœuds enfants.

Vous pouvez ensuite mettre en surbrillance la syntaxe en cours de route, en vous assurant que le balisage est bien formé (effectué automatiquement par le navigateur lors de l'ajout via innerHTML), etc.

4
aprilchild

Si vous recherchez une solution JavaScript, prenez simplement le code de l'outil Pretty Diff à l'adresse http://prettydiff.com/?m=beautify .

Vous pouvez également envoyer des fichiers à l'outil à l'aide du paramètre s, tels que: http://prettydiff.com/?m=beautify&s=https://stackoverflow.com/

4
austincheney

Vous pouvez obtenir un joli XML formaté avec xml-beautify

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

indent : motif d'indentation comme des espaces blancs

useSelfClosingElement : true => utilise un élément à fermeture automatique lorsqu'un élément est vide.

JSFiddle

Original (avant)

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

Embellie (après)

<?xml version="1.0" encoding="utf-8"?>
<example version="2.0">
  <head>
    <title>Original aTitle</title>
  </head>
  <body info="none" />
</example>
2
riversun
Or just print out the special HTML characters?

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


&#09;   Horizontal tab  
&#10;   Line feed
2
Tobias
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';
2
sanjaykumar

XMLSpectrum formate XML, prend en charge l'indentation des attributs et met également en surbrillance la syntaxe pour XML et toutes les expressions XPath incorporées:

XMLSpectrum formatted XML

XMLSpectrum est un projet open source, codé en XSLT 2.0 - vous pouvez donc l'exécuter côté serveur avec un processeur tel que Saxon-HE (recommandé) ou côté client à l'aide de Saxon-CE.

XMLSpectrum n'est pas encore optimisé pour s'exécuter dans le navigateur - d'où la recommandation d'exécuter ce côté serveur.

2
pgfearo

voici une autre fonction pour formater 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;
}
2
michael hancock

Utilisez la méthode ci-dessus pour de jolies impressions, puis ajoutez-la à n’importe quelle div en utilisant jquery text () method. Par exemple, id de div est xmldiv, puis utilisez:

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

2
Sanjeev Rathaur
var reg = /(>)\s*(<)(\/*)/g;
xml = xml.replace(/\r|\n/g, ''); //deleting already existing whitespaces
xml = xml.replace(reg, '$1\r\n$2$3');
1
Jason Im

https://www.npmjs.com/package/js-beautify

Cette bibliothèque fonctionne pour moi. Prise en charge de l'onglet, prise en charge de la version Web et du nœud. Prend également en charge JS, HTML, CSS. Aussi disponible en CDN.

1
ColacX