In XML đẹp bằng javascript


135

Tôi có một chuỗi đại diện cho một XML không thụt lề mà tôi muốn in đẹp. Ví dụ:

<root><node/></root>

nên trở thành:

<root>
  <node/>
</root>

Làm nổi bật cú pháp không phải là một yêu cầu. Để giải quyết vấn đề, trước tiên tôi chuyển đổi XML để thêm lợi nhuận vận chuyển và khoảng trắng và sau đó sử dụng thẻ trước để xuất XML. Để thêm dòng mới và khoảng trắng tôi đã viết hàm sau:

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

Sau đó tôi gọi hàm như thế này:

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

Điều này hoạt động hoàn toàn tốt cho tôi nhưng trong khi tôi đang viết chức năng trước đó tôi nghĩ rằng phải có một cách tốt hơn. Vì vậy, câu hỏi của tôi là bạn có biết cách nào tốt hơn được cung cấp một chuỗi XML để in nó trong một trang html không? Bất kỳ khung javascript và / hoặc plugin nào có thể thực hiện công việc đều được chào đón. Yêu cầu duy nhất của tôi là điều này phải được thực hiện ở phía khách hàng.


2
Để có đầu ra HTML ưa thích (hiển thị ala IE XML), hãy xem phép chuyển đổi XSLT được sử dụng trong XPath Visualizer. Bạn có thể tải xuống Trình hiển thị XPath tại: huttar.net/dimitre/XPV/TopXML-XPV.html
Dimitre Novatchev

/.+<\/\w[ucci>[*>$/ - xóa "+" trong RegExp này vì nó làm chậm mã trong một số công cụ JavaScript, cho các nút có "giá trị thuộc tính dài".
4esn0k

Câu trả lời:


58

Từ văn bản của câu hỏi, tôi có ấn tượng rằng một kết quả chuỗi được mong đợi , trái ngược với kết quả được định dạng HTML.

Nếu đúng như vậy, cách đơn giản nhất để đạt được điều này là xử lý tài liệu XML bằng cách chuyển đổi danh tính và bằng một <xsl:output indent="yes"/>hướng dẫn :

<xsl: bản định kiểu = "1.0"
 xmlns: xsl = "http://www.w3.org/1999/XSL/Transform">
 <xsl: output omit-xml-kê khai = "yes" indent = "yes" />

    <xsl: template match = "node () | @ *">
      <xsl: sao chép>
        <xsl: application-samples select = "node () | @ *" />
      </ xsl: sao chép>
    </ xsl: mẫu>
</ xsl: biểu định kiểu>

Khi áp dụng chuyển đổi này trên tài liệu XML được cung cấp:

<root> <nút /> </ root>

hầu hết các bộ xử lý XSLT (.NET XslCompiledTransform, Saxon 6.5.4 và Saxon 9.0.0.2, AltovaXML) tạo ra kết quả mong muốn:

<root>
  <nút />
</ root>

3
Nó trông giống như một giải pháp tuyệt vời. Có cách nào trình duyệt chéo để áp dụng chuyển đổi này trong javascript không? Tôi không có một kịch bản phía máy chủ để dựa vào.
Darin Dimitrov

2
Đúng. Nhìn vào Sarissa: dev.abiss.gr/sarissa và tại đây: xml.com/pub/a/2005/02/23/sarissa.html
Dimitre Novatchev

6
@ablmf: "Không hoạt động" là gì? "Chrome" là gì? Tôi chưa bao giờ nghe nói về bộ xử lý XSLT như vậy. Ngoài ra, nếu bạn xem ngày trả lời, trình duyệt Chrome không tồn tại vào thời điểm đó.
Dimitre Novatchev

3
@ablmf: Cũng lưu ý rằng câu hỏi này (và câu trả lời của tôi cho câu hỏi này) là lấy XML khá đẹp dưới dạng chuỗi (văn bản) chứ không phải HTML. Không có gì ngạc nhiên khi một chuỗi như vậy không hiển thị trong trình duyệt. Để có đầu ra HTML ưa thích (hiển thị ala IE XML), hãy xem phép chuyển đổi XSLT được sử dụng trong XPath Visualizer. Bạn có thể tải xuống Trình hiển thị XPath tại: huttar.net/dimitre/XPV/TopXML-XPV.html . Bạn có thể cần điều chỉnh mã một chút (chẳng hạn như để loại bỏ các chức năng mở rộng javascript để thu gọn / mở rộng một nút), nhưng nếu không thì HTML kết quả sẽ hiển thị tốt.
Dimitre Novatchev

2
JohnK, Năm 2008, khi câu hỏi này được trả lời, mọi người đã bắt đầu chuyển đổi XSLT từ JavaScript trong IE - gọi MSXML3. Bây giờ họ vẫn có thể làm điều này, mặc dù bộ xử lý XSLT đi kèm với IE11 là MSXML6. Tất cả các trình duyệt khác có khả năng tương tự, mặc dù chúng có bộ xử lý XSLT tích hợp khác nhau. Đây là lý do tại sao người hỏi ban đầu không bao giờ nêu câu hỏi như vậy.
Dimitre Novatchev 10/2/2015

32

Sửa đổi nhẹ chức năng javascript của efnx clckclcks. Tôi đã thay đổi định dạng từ dấu cách sang tab, nhưng quan trọng nhất là tôi cho phép văn bản duy trì trên một dòng:

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

bạn có thể vui lòng cập nhật chức năng của mình để xem xét nhận xét của Chuan Ma bên dưới không? Đã làm cho tôi. Cảm ơn. Chỉnh sửa: Tôi chỉ làm điều đó bản thân mình.
Louis LC

1
Xin chào, tôi đã cải thiện một chút chức năng của bạn để xử lý chính xác <?xml ... ?>khai báo tùy chọn ở đầu văn bản XML
lviggiani

31

Điều này có thể được thực hiện bằng các công cụ javascript gốc, không có lib của bên thứ 3, mở rộng câu trả lời của @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>'));

Đầu ra:

<root>
  <node/>
</root>

Câu đố

Lưu ý, như được chỉ ra bởi @ jat255, in đẹp <xsl:output indent="yes"/>không được firefox hỗ trợ. Nó dường như chỉ hoạt động trong chrome, opera và có lẽ các trình duyệt dựa trên webkit còn lại.


Câu trả lời rất hay, nhưng không may là Internet Explorer. Hãy tổ chức lại bữa tiệc.
Waruyama

thật tuyệt, nó chỉ hoạt động khi xml đầu vào là một dòng duy nhất ... nếu bạn không quan tâm đến nhiều dòng trong các nút văn bản, trước khi gọi prettify, hãy gọiprivate 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"), " "); }
Sasha Bond

5
Tôi nhận được một lỗi, nhưng lỗi không có tin nhắn. Nó cũng xảy ra trong fiddle, sử dụng firefox.
Tomáš Zato - Phục hồi Monica

Điều này cũng không hoạt động với tôi với một lỗi trống trong Firefox
jat255

1
Điều này được thảo luận tại: stackoverflow.com/questions/51989864/ , Rõ ràng, Firefox cần một đặc tả phiên bản cho xsl, nhưng dù sao thì việc triển khai Mozilla không tôn trọng bất kỳ xsl:outputthẻ nào , vì vậy bạn sẽ không nhận được tốt định dạng nào.
jat255

19

Cá nhân, tôi sử dụng google-code-prettify với chức năng này:

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

3
Oups, bạn cần thụt lề XML và google-code-prettify chỉ tô màu mã. lấy làm tiếc.
Touv

1
kết hợp prettify với smth như stackoverflow.com/questions/139076/ mẹo
Chris

3
Điều đó kết hợp với code.google.com/p/vkbeautify để thụt lề được thực hiện cho một kết hợp tốt.
Vdex

Đã chuyển từ mã google sang github. Liên kết mới: github.com/google/code-prettify
mUser1990

18

Tìm thấy chủ đề này khi tôi có một yêu cầu tương tự nhưng tôi đã đơn giản hóa mã của OP như sau:

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

làm việc cho tôi


Câu trả lời tốt nhất !!
Jcc.Sanabria

8

Hoặc nếu bạn chỉ thích một chức năng js khác để làm điều đó, tôi đã sửa đổi Darin (rất nhiều):

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

Tất cả các hàm javascript được cung cấp ở đây sẽ không hoạt động đối với tài liệu xml có khoảng trắng không xác định giữa thẻ kết thúc '>' và thẻ bắt đầu '<'. Để sửa chúng, bạn chỉ cần thay thế dòng đầu tiên trong các chức năng

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

bởi

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

4

Còn việc tạo một nút sơ khai (document.createEuity ('div') - hoặc sử dụng thư viện của bạn tương đương), điền vào chuỗi xml (thông qua InternalHTML) và gọi hàm đệ quy đơn giản cho phần tử gốc / hoặc phần tử gốc trong trường hợp bạn không có gốc. Hàm sẽ tự gọi cho tất cả các nút con.

Sau đó, bạn có thể tô sáng cú pháp trên đường đi, chắc chắn rằng đánh dấu được hình thành tốt (được trình duyệt tự động thực hiện khi nối thêm qua InternalHTML), v.v. Nó sẽ không đủ mã và có thể đủ nhanh.


1
Âm thanh như phác thảo cho một giải pháp tuyệt vời, thanh lịch. Làm thế nào về một thực hiện?
JohnK

4

Nếu bạn đang tìm kiếm một giải pháp JavaScript, chỉ cần lấy mã từ công cụ Pretty Diff tại http://prettydiff.com/?m=beautify

Bạn cũng có thể gửi tệp đến công cụ bằng tham số s, chẳng hạn như: http://prettydiff.com/?m=beautify&s=https://stackoverflow.com/


prettydiff là một công cụ thực sự tốt đẹp. Dưới đây là một số thông tin khác về cách sử dụng: stackoverflow.com/questions/19822460/pretty-diff-usage/ mẹo
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';

Sau khi vật lộn với câu trả lời kém chất lượng này, tôi đã làm cho nó hoạt động, tôi cho rằng - kết quả không đẹp lắm: không thụt vào.
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 định dạng XML, hỗ trợ thụt lề thuộc tính và cũng làm nổi bật cú pháp cho XML và bất kỳ biểu thức XPath nhúng nào:

XMLSpectrum định dạng XML

XMLSpectrum là một dự án nguồn mở, được mã hóa trong XSLT 2.0 - vì vậy bạn có thể chạy phía máy chủ này với bộ xử lý như Saxon-HE (được khuyến nghị) hoặc phía máy khách sử dụng Saxon-CE.

XMLSpectrum chưa được tối ưu hóa để chạy trong trình duyệt - do đó khuyến nghị nên chạy phía máy chủ này.


2

Sử dụng phương thức trên cho bản in đẹp và sau đó thêm phần này vào bất kỳ div nào bằng cách sử dụng phương thức jquery text () . ví dụ id của div xmldivthì sử dụng:

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


2
"Phương pháp trên cho bản in đẹp" là gì?
JW Lim

2

đây là một chức năng khác để định dạng 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

Bạn có thể nhận được xml được định dạng đẹp với xml-beautify

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

thụt lề : mô hình thụt lề như khoảng trắng

useSelfClosesEuity : true => sử dụng phần tử tự đóng khi phần tử trống.

Câu đố

Bản gốc (Trước)

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

Làm đẹp (Sau)

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

-1

Thư viện Xml-to-json có phương pháp formatXml(xml).Tôi là người duy trì dự án.

var prettyXml = formatXml("<a><b/></a>");

// <a>
//   <b/>
// </a>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.