Довольно печатать XML с помощью JavaScript


135

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

<root><node/></root>

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

<root>
  <node/>
</root>

Подсветка синтаксиса не является обязательной. Чтобы решить эту проблему, я сначала преобразовываю XML, чтобы добавить возврат каретки и пробелы, а затем использую предварительный тег для вывода 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-фреймворки и / или плагины, которые могут сделать эту работу, приветствуются. Мое единственное требование - это сделать на стороне клиента.


2
Для необычного вывода HTML (в виде отображения XML в формате IE) см. Преобразование XSLT, используемое в визуализаторе XPath. Вы можете скачать визуализатор XPath по адресу: huttar.net/dimitre/XPV/TopXML-XPV.html
Димитр Новачев

/.+<\/\w[^>]*>$/ - удалите «+» в этом RegExp, поскольку это замедляет код в некоторых механизмах JavaScript для узлов со «длинными значениями атрибутов».
4esn0k

Ответы:


58

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

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

<xsl: stylesheet version = "1.0"
 XMLNS: XSL = "http://www.w3.org/1999/XSL/Transform">
 <xsl: output omit-xml-объявление = "да" отступ = "да" />

    <xsl: template match = "node () | @ *">
      <XSL: копия>
        <xsl: apply-templates select = "node () | @ *" />
      </ XSL: копия>
    </ XSL: шаблон>
</ XSL: таблицы стилей>

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

<Корень> <узел /> </ Root>

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

<Корень>
  <узел />
</ Корень>

3
Похоже, отличное решение. Есть ли кросс-браузерный способ применить это преобразование в JavaScript? У меня нет серверного скрипта, на который можно было бы положиться.
Дарин Димитров


6
@ablmf: что "не работает"? Что такое "Хром"? Я никогда не слышал о таком процессоре XSLT. Кроме того, если вы посмотрите на дату ответа, браузер Chrome в то время не существовал.
Димитр Новатчев

3
@ablmf: Также обратите внимание, что этот вопрос (и мой ответ на него) состоит в том, чтобы получить симпатичный XML в виде строки (текста), а не HTML. Не удивительно, что такая строка не отображается в браузере. Для необычного вывода HTML (в виде отображения XML в формате IE) см. Преобразование XSLT, используемое в визуализаторе XPath. Вы можете загрузить визуализатор XPath по адресу: huttar.net/dimitre/XPV/TopXML-XPV.html . Возможно, вам придется немного изменить код (например, удалить функции расширения javascript для свертывания / развертывания узла), но в противном случае полученный HTML-код должен отображаться нормально.
Димитр Новатчев

2
JohnK, В 2008 году, когда на этот вопрос был дан ответ, люди инициировали XSLT-преобразования из JavaScript в IE - вызывая MSXML3. Теперь они все еще могут это сделать, хотя процессор XSLT, поставляемый с IE11, - MSXML6. Все остальные браузеры имеют аналогичные возможности, хотя имеют разные встроенные процессоры XSLT. Вот почему первоначальный аскер никогда не поднимал такой вопрос.
Димитр Новатчев

32

Небольшая модификация функции 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;
    };

Не могли бы вы обновить свою функцию, чтобы учесть комментарий Чуан Ма ниже? Работал на меня. Спасибо. Изменить: я просто сделал это сам.
Луи ЛК

1
Привет! Я немного улучшил вашу функцию, чтобы правильно обрабатывать необязательные <?xml ... ?>объявления в начале текста XML
lviggiani

31

Это можно сделать с помощью встроенных инструментов 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

Обратите внимание, как отмечает @ jat255, красивая печать с <xsl:output indent="yes"/>помощью firefox не поддерживается. Кажется, он работает только в браузерах Chrome, Opera и, вероятно, в остальных браузерах на основе webkit.


Очень хороший ответ, но, к сожалению, Internet Explorer снова портит вечеринку.
Варуяма

хорошо, это работает только тогда, когда входной xml представляет собой одну строку ... если вам не нужны множественные строки в текстовых узлах, перед вызовом prettify, позвонитеprivate 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"), " "); }
Саша Бонд

5
Я получаю сообщение об ошибке, но сообщение об ошибке отсутствует. Это происходит и в скрипке, используя firefox.
Томаш Зато - Восстановить Монику

Это также не работает для меня с пустой ошибкой в ​​Firefox
jat255

1
Это обсуждается по адресу: stackoverflow.com/questions/51989864/… Судя по всему, Firefox нужна спецификация версии для xsl, но это не имеет значения, так как реализация Mozilla не учитывает ни один xsl:outputтег, поэтому вы не получите хороший форматирование в любом случае.
jat255

19

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

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

3
Упс, вам нужно сделать отступ в XML и gtt-code-prettify только раскрасить код. Прости.
Touv


3
Это в сочетании с code.google.com/p/vkbeautify для отступов сделано для хорошей комбинации.
Vdex

Перемещено из кода Google в github. Новая ссылка: github.com/google/code-prettify
mUser1990

18

Нашел этот поток, когда у меня было похожее требование, но я упростил код 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);
}

работает для меня!


Лучший ответ !!
Jcc.Sanabria

8

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

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

6

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

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

по

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

4

как насчет создания узла-заглушки (document.createElement ('div') - или с использованием вашего библиотечного эквивалента), заполнения его строкой xml (через innerHTML) и вызова простой рекурсивной функции для корневого элемента / или элемента-заглушки на случай, если вы нет рута Функция будет вызывать себя для всех дочерних узлов.

Затем вы можете выделить синтаксис по пути, быть уверенным, что разметка правильно сформирована (это делается браузером автоматически при добавлении через innerHTML) и т. Д. Это не будет так много кода и, вероятно, достаточно быстро.


1
Походит на схему для удивительного, изящного решения. Как насчет реализации?
JohnK

4

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

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


prettydiff - действительно хороший инструмент. Вот еще немного информации об использовании: stackoverflow.com/questions/19822460/pretty-diff-usage/…
боб

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

После борьбы с этим плохо сформированным ответом я заставил его работать, я полагаю - результаты не очень красивые: без отступов.
JohnK

2
Or just print out the special HTML characters?

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


&#09;   Horizontal tab  
&#10;   Line feed

2

XMLSpectrum форматирует XML, поддерживает отступы атрибутов, а также делает подсветку синтаксиса для XML и любых встроенных выражений XPath:

XMLSpectrum отформатированный XML

XMLSpectrum - это проект с открытым исходным кодом, закодированный в XSLT 2.0 - так что вы можете запустить эту сторону сервера с таким процессором, как Saxon-HE (рекомендуется) или со стороны клиента, используя Saxon-CE.

XMLSpectrum еще не оптимизирован для работы в браузере, поэтому рекомендуется запускать его на стороне сервера.


2

Используйте приведенный выше метод для красивой печати, а затем добавьте его в любой div с помощью метода jquery text () . например, идентификатор div xmldivзатем использовать:

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


2
Какой "метод выше для красивой печати"?
JW Lim

2

вот еще одна функция для форматирования 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

Вы можете получить довольно отформатированный XML с помощью XML-украшения

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

отступ : шаблон отступа, как пробелы

useSelfClosingElement : true => использовать самозакрывающийся элемент, когда пустой элемент.

JSFiddle

Original (Перед)

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

Приукрашена (После)

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


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.