Гарне друк 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.