자바 스크립트로 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.