Довольно печатать XML с помощью JavaScript - PullRequest
113 голосов
/ 18 декабря 2008

У меня есть строка, представляющая XML-код без отступа, который я хотел бы распечатать. Например:

<root><node/></root>

должно стать:

<root>
  <node/>
</root>

Подсветка синтаксиса не является обязательной. Чтобы решить эту проблему, я сначала преобразовываю XML, чтобы добавить возврат каретки и пробелы, а затем использую тег pre для вывода XML. Чтобы добавить новые строки и пробелы, я написал следующую функцию:

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

Затем я вызываю функцию следующим образом:

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

Это прекрасно работает для меня, но когда я писал предыдущую функцию, я думал, что должен быть лучший способ. Итак, мой вопрос: знаете ли вы какой-нибудь лучший способ дать XML-строку, чтобы красиво распечатать ее на HTML-странице? Любые JavaScript-фреймворки и / или плагины, которые могут сделать эту работу, приветствуются. Мое единственное требование - это делать на стороне клиента.

Ответы [ 19 ]

61 голосов
/ 19 января 2012

рассмотрите возможность использования плагина vkBeautify

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

написано на простом javascript, очень маленьком: менее 1,5 КБ, если минимизировано, очень быстро: менее 5 мсек. обрабатывать 50K XML-текст.

54 голосов
/ 18 декабря 2008

Из текста вопроса Создается впечатление, что ожидается строковый результат , а не результат в формате HTML.

Если это так, самый простой способ добиться этого - обработать XML-документ с помощью преобразования идентификаторов и с помощью инструкции <xsl:output indent="yes"/> :

<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="node()|@*"/>
      </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

При применении этого преобразования к предоставленному документу XML:

<root><node/></root>

большинство процессоров XSLT (.NET XslCompiledTransform, Saxon 6.5.4 и Saxon 9.0.0.2, AltovaXML) дают желаемый результат:

<root>
  <node />
</root>
31 голосов
/ 24 мая 2010

Небольшая модификация функции javascript в efnx clckclcks. Я изменил форматирование с пробелов на табуляцию, но самое главное, я позволил тексту остаться в одной строке:

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;
    };
19 голосов
/ 29 июля 2009

Personnaly, я использую google-code-prettify с этой функцией:

prettyPrintOne('<root><node1><root>', 'xml')
16 голосов
/ 16 ноября 2017

Это можно сделать с помощью встроенных инструментов javascript, без сторонних библиотек, расширяя ответ @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>'));

Выходы:

<root>
  <node/>
</root>

JSFiddle

11 голосов
/ 24 марта 2018

Нашел эту ветку, когда у меня было похожее требование, но я упростил код OP следующим образом:

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

у меня работает!

8 голосов
/ 24 марта 2010

Или, если вам просто нужна другая функция js, я изменил (много) по Дарину:

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;
};
7 голосов
/ 22 января 2015

Эта библиотека делает именно то, что вы хотите!

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

6 голосов
/ 02 мая 2012

Все приведенные здесь функции javascript не будут работать для документа xml, имеющего неопределенные пробелы между конечным тегом «>» и начальным тегом «<». Чтобы их исправить, нужно просто заменить первую строку в функциях </p>

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

от

var reg = /(>)\s*(<)(\/*)/g;
4 голосов
/ 27 ноября 2011

Если вы ищете решение JavaScript, просто возьмите код из инструмента Pretty Diff по адресу http://prettydiff.com/?m=beautify

Вы также можете отправлять файлы в инструмент, используя параметр s, например: http://prettydiff.com/?m=beautify&s=https://stackoverflow.com/

...