자바 스크립트로 XML 인쇄하기


135

예쁜 인쇄하려는 들여 쓰기되지 않은 XML을 나타내는 문자열이 있습니다. 예를 들면 다음과 같습니다.

<root><node/></root>

되어야한다 :

<root>
  <node/>
</root>

구문 강조는 필수 사항이 아닙니다. 문제를 해결하기 위해 먼저 캐리지 리턴과 공백을 추가하도록 XML을 변환 한 다음 pre 태그를 사용 하여 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 페이지에서 예쁘게 인쇄하는 더 좋은 방법을 알고 있습니까? 작업을 수행 할 수있는 모든 자바 스크립트 프레임 워크 및 / 또는 플러그인을 환영합니다. 내 유일한 요구 사항은 클라이언트 측에서 수행해야합니다.


2
멋진 HTML 출력 (ala IE XML 표시)은 XPath Visualizer에서 사용되는 XSLT 변환을 참조하십시오. XPath Visualizer는 다음 사이트
Dimitre Novatchev

/.+<\/\w[^>]*>$/-이 RegExp에서 "+"를 제거하면 "long attribute values"가있는 노드의 일부 JavaScript 엔진에서 코드 속도가 느려집니다.
4esn0k

답변:


58

질문의 텍스트 에서 HTML 형식의 결과와 달리 문자열 결과가 예상된다는 인상을 얻습니다 .

그렇다면 , 가장 간단한 방법은 ID 변환<xsl:output indent="yes"/>지시 사항으로 XML 문서를 처리하는 것입니다 .

<xsl : stylesheet version = "1.0"
 xmlns : xsl = "http://www.w3.org/1999/XSL/Transform">
 <xsl : 출력 omit-xml-declaration = "yes"indent = "yes"/>

    <xsl : template match = "node () | @ *">
      <xsl : 복사>
        <xsl : apply-templates select = "node () | @ *"/>
      </ xsl : copy>
    </ xsl : template>
</ xsl : stylesheet>

제공된 XML 문서에이 변환을 적용 할 때 :

<root> <노드 /> </ root>

대부분의 XSLT 프로세서 (.NET XslCompiledTransform, Saxon 6.5.4 및 Saxon 9.0.0.2, AltovaXML)는 원하는 결과를 생성합니다.

<루트>
  <노드 />
</ root>

3
훌륭한 솔루션처럼 보입니다. 이 변환을 자바 스크립트에 적용하는 크로스 브라우저 방법이 있습니까? 사용할 서버 쪽 스크립트가 없습니다.
Darin Dimitrov


6
@ablmf : "작동하지 않는"것은 무엇입니까? "Chrome"이란 무엇입니까? 그런 XSLT 프로세서에 대해 들어 본 적이 없습니다. 또한 답변 날짜를 확인한 경우 당시에는 Chrome 브라우저가 없었습니다.
미트 르 노바 체프

3
@ablmf : 또한이 질문 (그리고 그것에 대한 대답)은 HTML이 아닌 문자열 (텍스트)로 잘 꾸며진 XML을 얻는 것입니다. 그러한 문자열이 브라우저에 표시되지 않는 것은 당연합니다. 멋진 HTML 출력 (ala IE XML 표시)은 XPath Visualizer에서 사용되는 XSLT 변환을 참조하십시오. XPath Visualizer는 huttar.net/dimitre/XPV/TopXML-XPV.html 에서 다운로드 할 수 있습니다 . 노드 축소 / 확장을 위해 자바 스크립트 확장 함수를 제거하는 등 코드를 약간 조정해야 할 수도 있지만 그렇지 않으면 결과 HTML이 제대로 표시되어야합니다.
Dimitre Novatchev

2
JohnK, 2008 년에이 질문에 대한 답변을들은 사람들은 MSXML3을 호출하여 IE의 JavaScript에서 XSLT 변환을 시작했습니다. IE11과 함께 제공되는 XSLT 프로세서는 MSXML6이지만 이제는 여전히이 작업을 수행 할 수 있습니다. 내장 된 XSLT 프로세서는 다르지만 다른 모든 브라우저의 기능은 비슷합니다. 이것이 최초의 질문자가 결코 그런 질문을 제기하지 않은 이유입니다.
Dimitre Novatchev

32

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

아래 Chuan Ma의 의견을 고려하여 기능을 업데이트 할 수 있습니까? 나를 위해 일했다. 감사. 편집 : 방금 직접했습니다.
Louis LC

1
안녕하세요, <?xml ... ?>XML 텍스트의 시작 부분에서 선택적 선언 을 올바르게 처리하기 위해 함수를 약간 개선했습니다.
lviggiani

31

이것은 타사 라이브러리없이 기본 자바 스크립트 도구를 사용하여 수행 할 수 있으며 @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에서 지적했듯이 firefox에서는 예쁜 인쇄를 <xsl:output indent="yes"/>지원하지 않습니다. 크롬, 오페라 및 아마도 나머지 웹킷 기반 브라우저에서만 작동하는 것 같습니다.


아주 좋은 대답이지만 불행히도 Internet Explorer는 파티를 다시 망칩니다.
Waruyama

좋은 점은 입력 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"), " "); }
Sasha Bond

5
오류가 발생하지만 오류 메시지가 없습니다. 파이어 폭스를 사용하여 바이올린에서도 발생합니다.
Tomáš Zato-복원 모니카

Firefox에서도 빈 오류로 작동하지 않습니다.
jat255

1
이것은 stackoverflow.com/questions/51989864/ 에서 논의됩니다. 분명히 Firefox에는 xsl에 대한 버전 사양이 필요하지만 Mozilla 구현이 xsl:output태그를 존중하지 않기 때문에 어쨌든 중요하지 않습니다. 어쨌든 포맷.
jat255

19

개인적으로, 나는 이 기능으로 google-code-prettify를 사용합니다 :

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

3
XML을 들여 쓰기하고 google-code-prettify는 코드를 채색해야합니다. 죄송합니다.
Touv


3
좋은 조합을 위해 들여 쓰기를 위해 code.google.com/p/vkbeautify 와 결합되었습니다 .
Vdex

구글 코드에서 github로 이동. 새로운
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

여기에 주어진 모든 자바 스크립트 함수는 종료 태그 '>'와 시작 태그 '<'사이에 공백이 지정되지 않은 xml 문서에서 작동하지 않습니다. 그것들을 고치려면 함수의 첫 번째 줄만 바꾸면됩니다.

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

으로

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

4

스텁 노드 (document.createElement ( 'div')-또는 동등한 라이브러리 사용)를 작성하고 XML 문자열 (innerHTML을 통해)을 채우고 루트 요소 또는 스텁 요소에 대해 간단한 재귀 함수를 호출하는 경우는 어떻습니까? 뿌리가 없습니다. 이 함수는 모든 자식 노드에 대해 스스로를 호출합니다.

그런 다음 길을 따라 구문을 강조 표시하고 마크 업이 올바르게 구성되었는지 확인하십시오 (내부 HTML을 통해 추가 할 때 브라우저가 자동으로 수행함) 등 코드가 많지 않고 충분히 빠릅니다.


1
놀랍고 우아한 솔루션의 개요처럼 들립니다. 구현은 어떻습니까?
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';

이 잘못된 형식의 답변으로 어려움을 겪은 후에는 효과가 있다고 생각합니다. 결과는 그리 예쁘지 않습니다. 들여 쓰기가 없습니다.
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

예쁜 인쇄를 위해 위의 방법을 사용한 다음 jquery text () 메서드를 사용하여 div에 추가하십시오 . 예를 들어 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-beautify로 예쁜 형식의 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');

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.