148 votes

Jolie impression XML avec javascript

J'ai une chaîne qui représente un non indenté XML que je voudrais pretty-print. Par exemple:

<root><node/></root>

devrait devenir:

<root>
  <node/>
</root>

La coloration syntaxique n'est pas une exigence. Pour résoudre le problème j'ai d'abord transformer le XML pour ajouter des retours chariot et les espaces blancs et ensuite utiliser un pré balise de sortie XML. Pour ajouter de nouvelles lignes et de nouveaux espaces en blanc, 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'ai ensuite appeler la fonction comme ceci:

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

Cela fonctionne parfaitement bien pour moi mais quand j'ai écrit la fonction précédente j'ai pensé qu'il doit y avoir une meilleure façon. Donc ma question est connaissez-vous une meilleure façon étant donné une chaîne XML pour pretty-print dans une page html? Tout frameworks javascript et/ou plugins qui pourraient faire le travail sont les bienvenus. Ma seule exigence est de ce fait sur le côté client.

62voto

vadimk Points 633

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.

60voto

Dimitre Novatchev Points 147842

À partir du texte de la question , j'ai l'impression qu'une chaîne de caractères résultat escompté, par opposition à un format HTML résultat.

Si c'est le cas, la façon la plus simple d'y parvenir est de traiter le document XML avec la transformation de l'identité et avec un <xsl:output indent="yes"/> enseignement:

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

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

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

<root><node/></root>

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

<root>
 nœud </>
</root>

33voto

Dan BROOKS Points 91

Légère modification de la fonction javascript de efnx clckclcks. J'ai modifié 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 = /(>)(<)(\/*)/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 += '\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;
    };
 

18voto

Touv Points 715

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

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

9voto

schellsan Points 746

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X