Javascript ile güzel baskı XML


135

Ben güzel yazdırmak istiyorum girintili olmayan bir XML temsil eden bir dize var. Örneğin:

<root><node/></root>

olmalı:

<root>
  <node/>
</root>

Sözdizimi vurgulama bir zorunluluk değildir. Sorunu çözmek için önce XML'i satır başı ve boşluklar eklemek üzere dönüştürdüm ve ardından XML çıktısını almak için bir ön etiket kullanıyorum . Yeni çizgiler ve beyaz boşluklar eklemek için aşağıdaki işlevi yazdım:

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

Sonra işlevi şöyle çağırır:

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

Bu benim için mükemmel bir şekilde çalışıyor ama önceki işlevi yazarken daha iyi bir yol olması gerektiğini düşündüm. Yani benim sorum bir html sayfasında güzel yazdırmak için bir XML dizesi verilen daha iyi bir yol biliyor musunuz? İşi yapabilecek herhangi bir javascript çerçevesi ve / veya eklentisi kabul edilir. Tek ihtiyacım bu müşteri tarafında yapılması.


2
Süslü bir HTML çıktısı (ala IE XML ekranı) için, XPath Visualizer'da kullanılan XSLT dönüşümüne bakın. XPath Görüntüleyiciyi şu
adresten indirebilirsiniz

/.+<\/\w^^> Cialis*>$/ - "Uzun öznitelik değerlerine" sahip düğümler için bazı JavaScript motorlarındaki kodu yavaşlattığından bu RegExp'deki "+" işaretini kaldırın.
4esn0k

Yanıtlar:


58

Sorunun metninden, HTML biçimli bir sonucun aksine, bir dize sonucunun beklendiği izlenimini edindim.

Bu durumda, bunu başarmanın en basit yolu XML belgesini kimlik dönüşümü ve bir <xsl:output indent="yes"/>talimatla işlemektir :

<xsl: stil sayfası sürümü = "1.0"
 xmlns: xsl = "http://www.w3.org/1999/XSL/Transform">
 <xsl: çıktı omit-xml-declaration = "evet" girinti = "evet" />

    <xsl: template match = "düğüm () | @ *">
      <Xsl: copy>
        <xsl: uygula-şablonları select = "düğüm () | @ *" />
      </ Xsl: copy>
    </ Xsl: template>
</ Xsl: stylesheet>

Bu dönüşümü sağlanan XML belgesine uygularken:

<Kök> <düğüm /> </ root>

çoğu XSLT işlemci (.NET XslCompiledTransform, Saxon 6.5.4 ve Saxon 9.0.0.2, AltovaXML) istenen sonucu üretir:

<Kök>
  <düğüm />
</ Root>

3
Harika bir çözüm gibi görünüyor. Javascript bu dönüşümü uygulamak için çapraz tarayıcı yolu var mı? Güvenecek bir sunucu tarafı komut dosyam yok.
Darin Dimitrov


6
@ablmf: "Çalışmayan" nedir? "Chrome" nedir? Hiç böyle bir XSLT işlemci duymadım. Ayrıca, yanıtın tarihine bir göz attıysanız, Chrome tarayıcı o sırada yoktu.
Dimitre Novatchev

3
@ablmf: Ayrıca bu sorunun (ve benim cevabım) güzelleştirilmiş XML'i HTML değil bir dize (metin) olarak almak olduğunu unutmayın. Böyle bir dizenin tarayıcıda görünmemesine şaşmamalı. Süslü bir HTML çıktısı (ala IE XML ekranı) için, XPath Visualizer'da kullanılan XSLT dönüşümüne bakın. XPath Visualizer'ı huttar.net/dimitre/XPV/TopXML-XPV.html adresinden indirebilirsiniz . Kodu biraz ayarlamanız gerekebilir (örneğin bir düğümü daraltmak / genişletmek için javascript uzantı işlevlerini kaldırmak gibi), ancak aksi takdirde sonuçta elde edilen HTML düzgün görünmelidir.
Dimitre Novatchev

2
JohnK, 2008 yılında, bu soru cevaplandığında, insanlar IE'de JavaScript'ten XSLT dönüşümleri başlatıyordu - MSXML3'ü çağırıyor. IE11 ile gelen XSLT işlemci MSXML6 olsa da şimdi bunu hala yapabilirler. Farklı yerleşik XSLT işlemcilere sahip olmalarına rağmen, diğer tüm tarayıcıların benzer özellikleri vardır. Bu yüzden orijinal asker hiç böyle bir soru sormadı.
Dimitre Novatchev

32

Efnx clckclcks'ın javascript işlevinde küçük bir değişiklik. Biçimlendirmeyi boşluklardan sekmeye değiştirdim, ancak en önemlisi metnin bir satırda kalmasına izin verdim:

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

Chuan Ma'nın aşağıdaki yorumunu dikkate almak için lütfen fonksiyonunuzu güncelleyebilir misiniz? Benim için çalıştı. Teşekkürler. Düzenleme: Sadece kendim yaptım.
Louis LC

1
Merhaba, <?xml ... ?>XML metninin başında isteğe bağlı bildirimi doğru bir şekilde işlemek için işlevinizi biraz geliştirdim
lviggiani

31

Bu, @jimitre Novatchev'in cevabını genişleterek 3. taraf kütüphaneleri olmadan yerel javascript araçları kullanılarak yapılabilir:

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

Çıktılar:

<root>
  <node/>
</root>

JSFiddle

@ Jat255 tarafından işaret edildiği gibi, güzel baskıların <xsl:output indent="yes"/>firefox tarafından desteklenmediğini unutmayın. Sadece krom, opera ve muhtemelen geri kalan webkit tabanlı tarayıcılarda çalışıyor gibi görünüyor.


Çok güzel bir cevap, ama ne yazık ki Internet Explorer.
Waruyama

güzel, sadece giriş xml tek bir satır olduğunda çalışır ... metin düğümlerinde çoklu satırları umursamıyorsanız, güzelliği aramadan önce arayınprivate 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
Bir hata alıyorum, ancak hatanın mesajı yok. Firefox kullanarak kemanda da olur.
Tomáš Zato - Monica'yı geri yükle

Bu da Firefox'ta boş bir hata ile benim için çalışmıyor
jat255

1
Bu tartışılmaktadır: stackoverflow.com/questions/51989864/… Görünüşe göre, Firefox'un xsl için bir sürüm spesifikasyonuna ihtiyacı var, ancak yine de önemli değil, çünkü Mozilla uygulaması herhangi bir xsl:outputetikete saygı göstermiyor , bu nedenle yine de biçimlendirme.
jat255

19

Personnaly, google-code-prettify bu işlevle kullanıyorum:

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

3
Çorbalar, XML'i girintilemeniz ve google-kodu-güzelleştirmek sadece kodu renklendirmeniz gerekiyor. afedersiniz.
Touv

1
prettify'i ikramiye ile birleştirmek stackoverflow.com/questions/139076/…
Chris

3
Bu , iyi bir kombo için yapılan girinti için code.google.com/p/vkbeautify ile birleştirildi .
Vdex

Google kodundan github'a taşındı.Yeni bağlantı: github.com/google/code-prettify
mUser1990

18

Ben benzer bir gereksinimi vardı ama OP kodunu aşağıdaki gibi basitleştirilmiş zaman bu iş parçacığı bulundu:

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

benim için çalışıyor!


En iyi cevap !!
Jcc.Sanabria

8

Ya da sadece başka bir js işlevini yapmak istiyorsanız, Darin'i (çok) değiştirdim:

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

Burada verilen javascript işlevlerinin tümü, '>' bitiş etiketi ile '<' başlangıç ​​etiketi arasında belirtilmemiş beyaz boşluklara sahip bir xml belgesi için çalışmaz. Onları düzeltmek için işlevlerdeki ilk satırı değiştirmeniz yeterlidir

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

tarafından

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

4

saplama düğümü oluşturma (document.createElement ('div') - veya kitaplık eşdeğerini kullanma), xml dizesiyle doldurma (innerHTML aracılığıyla) ve siz durumda kök öğe / veya saplama öğesi için basit özyinelemeli işlev çağırma bir kökü yok. İşlev, tüm alt düğümler için kendisini çağırır.

Daha sonra sözdizimi vurgulamak olabilir yol boyunca, işaretleme iyi biçimlendirilmiş olduğundan emin olun (innerHTML aracılığıyla eklerken tarayıcı tarafından otomatik olarak yapılır) vb. Bu kadar kod ve muhtemelen yeterince hızlı olmaz.


1
İnanılmaz, zarif bir çözüm için anahat gibi görünüyor. Bir uygulamaya ne dersiniz?
JohnK


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

Bu zayıf biçimlendirilmiş cevapla mücadele ettikten sonra, işe yaradım, sanırım - sonuçlar çok hoş değil: girinti yok.
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 biçimlendirir, öznitelik girintisini destekler ve ayrıca XML ve tüm katıştırılmış XPath ifadeleri için sözdizimi vurgulama yapar:

XMLSpectrum biçimli XML

XMLSpectrum, XSLT 2.0 ile kodlanmış açık kaynaklı bir projedir - böylece bu sunucu tarafını Saxon-HE (önerilir) veya Saxon-CE kullanarak istemci tarafı gibi bir işlemci ile çalıştırabilirsiniz.

XMLSpectrum henüz tarayıcıda çalışacak şekilde optimize edilmemiştir;


2

Güzel yazdırma için yukarıdaki yöntemi kullanın ve sonra bunu jquery text () yöntemini kullanarak herhangi bir div öğesine ekleyin . örneğin div kimliği id xmldiv:

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


2
Ne "güzel baskı için yukarıdaki yöntem"?
JW Lim

2

xml formatlamak için başka bir fonksiyon

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-beautify ile oldukça biçimlendirilmiş xml alabilirsiniz

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

girinti : beyaz boşluklar gibi girinti deseni

useSelfClosingElement : true => boş öğe olduğunda kendiliğinden kapanan öğeyi kullan.

JSFiddle

Orjinal (Önce)

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

Beautified (Sonra)

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

Xml-json kütüphane yöntemi formatXml(xml).ben projenin sürdürücüsü vardır.

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

// <a>
//   <b/>
// </a>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.