Гарне друк 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 (ala IE XML display) див. Перетворення XSLT, що використовується у XPath Visualizer. Завантажити візуалізатор XPath можна за посиланням: huttar.net/dimitre/XPV/TopXML-XPV.html
Димитрей Новатчев,

/.+<\/\w evidence^>Sense*>$/ - видаліть "+" з цього RegExp, оскільки він уповільнює код у деяких двигунах JavaScript, для вузлів із "довгими значеннями атрибутів".
4esn0k

Відповіді:


58

З тексту запитання я створюю враження, що очікується рядковий результат , на відміну від результату у форматі HTML.

Якщо це так, найпростіший спосіб досягти цього - це обробити документ XML з перетворенням ідентичності та <xsl:output indent="yes"/>інструкцією :

<xsl: версія стилю = "1.0"
 xmlns: xsl = "http://www.w3.org/1999/XSL/Transform">
 <xsl: вихід omit-xml -кларація = "так" відступ = "так" />

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

Застосовуючи це перетворення на наданому документі XML:

<root><node/> </root>

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

<корень>
  <вузол />
</root>

3
Це виглядає як чудове рішення. Чи існує спосіб перехресного перегляду браузера застосувати це перетворення у javascript? У мене немає сценарію на стороні сервера, на який можна покластися.
Дарин Димитров

2
Так. Подивіться на Саріссу: dev.abiss.gr/sarissa і тут: xml.com/pub/a/2005/02/23/sarissa.html
Димитрей Новатчев

6
@ablmf: Що "не працює"? Що таке "Chrome"? Я ніколи не чув про такий процесор XSLT. Крім того, якщо ви подивитесь на дату відповіді, браузер Chrome на той час не існував.
Димитрій Новатчев

3
@ablmf: Також зауважте, що це питання (і моя відповідь на нього) полягає в тому, щоб отримати вдосконалений XML як рядок (текст), а не HTML. Недарма така рядок не відображається в браузері. Для вигадливого виводу HTML (ala IE XML display) див. Перетворення XSLT, що використовується у XPath Visualizer. Завантажити візуалізатор 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. Це, здається, працює лише в хромованих, оперних та, ймовірно, рештах браузерах на базі веб-файлів.


Дуже приємна відповідь, але, на жаль, Internet Explorer.знову надсилає партію
Варуяма

приємно, він працює лише тоді, коли введення xml - це єдиний рядок ... якщо вам не байдуже багато рядків у текстових вузлах, перед тим, як зателефонувати, зателефонуйте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-pretify з цією функцією:

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

3
На жаль, вам потрібно відступити XML і google-код, щоб вдосконалити лише кольоровий код. вибачте.
Touv

1
поєднуй гарненьку з smth, як stackoverflow.com/questions/139076/…
Кріс,

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, я змінив Даріна (багато):

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
Звучить, як контур дивовижного, елегантного рішення. Як щодо реалізації?
ДжонК

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/…
bob

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

Після боротьби з цією погано сформованою відповіддю я припустив, що вона працює, мабуть - результати не дуже гарні: немає відступів.
ДжонК

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

Використовуйте вище метод для симпатичного друку, а потім додайте це у будь-який розділ, використовуючи метод jquery text () . наприклад, id 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

Оригінал (раніше)

<?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>

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.