用 javascript 漂亮地打印 XML

我有一个字符串,它表示一个未缩进的 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 框架和/或插件。我唯一的要求是在客户端这样做。

184193 次浏览

如何创建一个存根节点(document.createElement (‘ div’)-或者使用库等价物) ,使用 xml 字符串(通过 innerHTML)填充它,并在没有根元素的情况下调用根元素/或存根元素的简单递归函数。该函数将对所有子节点调用自己。

然后您可以沿途进行语法高亮显示,确保标记是格式良好的(通过 innerHTML 附加时由浏览器自动完成)等等。它不会有那么多代码,而且可能足够快。

来自问题 我得到的印象是,一个字符串结果是预期的的文本,而不是 HTML 格式的结果。

如果是这样,实现这一点的最简单方法是使用 < a href = “ http://www.w3.org/TR/xslt # copy”rel = “ noReferrer”> 身份转换 和使用 < a href = “ http://www.w3.org/TR/xslt # section-XML-Output-Method”rel = “ noReferrer”> <xsl:output indent="yes"/> 指令来处理 XML 文档:

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


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

在对提供的 XML 文档应用此转换时:

<root><node/></root>

大多数 XSLT 处理器(. NET XslCompiledTransform,Saxon 6.5.4和 Saxon 9.0.0.2,AltovaXML)产生所需的结果:

<root>
<node />
</root>

就我个人而言,我使用 谷歌代码,美化来实现以下功能:

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

或者如果你只是想要另一个 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;
};

对 efnx clckclicks 的 javascript 函数稍作修改。我将格式从空格改为制表符,但最重要的是,我允许文本保持在一行上:

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;
};
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';
Or just print out the special HTML characters?


Ex: <xmlstuff>&#10; &#09;<node />&#10;</xmlstuff>




&#09;   Horizontal tab
&#10;   Line feed

如果你正在寻找一个 JavaScript 解决方案,只需要从 http://prettydiff.com/?m=beautify的漂亮差异工具中获取代码

您还可以使用 s 参数向工具发送文件,例如: Http://prettydiff.com/?m=beautify&s=https://stackoverflow.com/

这里给出的所有 javascript 函数都不适用于在结束标记“ >”和开始标记“ <”之间具有未指定空格的 xml 文档。要修复它们,只需替换函数中的第一行

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

作者

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

XMLSpectrum 格式化 XML,支持属性缩进,还可以对 XML 和任何嵌入式 XPath 表达式进行语法突出显示:

XMLSpectrum formatted XML

XMLSpectrum 是一个开源项目,编码在 XSLT 2.0中,因此您可以使用 Saxon-HE (推荐)等处理器在服务器端运行,或者使用 Saxon-CE 在客户端运行。

XMLSpectrum 还没有优化到可以在浏览器中运行——因此建议运行这个服务器端。

var reg = /(>)\s*(<)(\/*)/g;
xml = xml.replace(/\r|\n/g, ''); //deleting already existing whitespaces
xml = xml.replace(reg, '$1\r\n$2$3');

使用上面的方法进行漂亮打印,然后使用 jquery Text ()方法将其添加到任何 div 中。例如,div 的 id 是 xmldiv,然后使用:

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

下面是另一个格式化 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;
}

这可以通过使用本地的 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所指出的,Firefox 不支持使用 <xsl:output indent="yes"/>进行漂亮的打印。它似乎只能在 chrome、 Opera 以及其他基于 webkit 的浏览器中使用。

发现这个线程时,我有一个类似的需求,但我简化 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);
}

为我工作!

您可以使用 Xml-beautify获得格式非常漂亮的 xml

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

缩进 : 像空格一样的缩进模式

UseSelfClosingElement : true = > 在空元素时使用 self-close 元素。

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>

XML-to-json 库 has method formatXml(xml). I 是该项目的维护者。

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


// <a>
//   <b/>
// </a>

这是我的版本,可能对其他人有用,使用字符串构建器 看到有人有同样的代码。

    public String FormatXml(String xml, String tab)
{
var sb = new StringBuilder();
int indent = 0;
// find all elements
foreach (string node in Regex.Split(xml,@">\s*<"))
{
// if at end, lower indent
if (Regex.IsMatch(node, @"^\/\w")) indent--;
sb.AppendLine(String.Format("{0}<{1}>", string.Concat(Enumerable.Repeat(tab, indent).ToArray()), node));
// if at start, increase indent
if (Regex.IsMatch(node, @"^<?\w[^>]*[^\/]$")) indent++;
}
// correct first < and last > from the output
String result = sb.ToString().Substring(1);
return result.Remove(result.Length - Environment.NewLine.Length-1);
}

对于当前的一个项目,我需要在没有额外库的情况下对 XML 进行美化和着色。下面的自包含代码工作得非常好。

function formatXml(xml,colorize,indent) {
function esc(s){return s.replace(/[-\/&<> ]/g,function(c){         // Escape special chars
return c==' '?'&nbsp;':'&#'+c.charCodeAt(0)+';';});}
var sm='<div class="xmt">',se='<div class="xel">',sd='<div class="xdt">',
sa='<div class="xat">',tb='<div class="xtb">',tc='<div class="xtc">',
ind=indent||'  ',sz='</div>',tz='</div>',re='',is='',ib,ob,at,i;
if (!colorize) sm=se=sd=sa=sz='';
xml.match(/(?<=<).*(?=>)|$/s)[0].split(/>\s*</).forEach(function(nd){
ob=('<'+nd+'>').match(/^(<[!?\/]?)(.*?)([?\/]?>)$/s);             // Split outer brackets
ib=ob[2].match(/^(.*?)>(.*)<\/(.*)$/s)||['',ob[2],''];            // Split inner brackets
at=ib[1].match(/^--.*--$|=|('|").*?\1|[^\t\n\f \/>"'=]+/g)||['']; // Split attributes
if (ob[1]=='</') is=is.substring(ind.length);                     // Decrease indent
re+=tb+tc+esc(is)+tz+tc+sm+esc(ob[1])+sz+se+esc(at[0])+sz;
for (i=1;i<at.length;i++) re+=(at[i]=="="?sm+"="+sz+sd+esc(at[++i]):sa+' '+at[i])+sz;
re+=ib[2]?sm+esc('>')+sz+sd+esc(ib[2])+sz+sm+esc('</')+sz+se+ib[3]+sz:'';
re+=sm+esc(ob[3])+sz+tz+tz;
if (ob[1]+ob[3]+ib[2]=='<>') is+=ind;                             // Increase indent
});
return re;
}

参见 https://jsfiddle.net/dkb0La16/

你也可以使用 Saxon-JS 客户端:

<script src="SaxonJS/SaxonJS2.js"></script>


<script>
let myXML = `<root><node/></root>`;


SaxonJS.getResource({
text: myXML.replace(`xml:space="preserve"`, ''),
type: "xml"
}).then(doc => {
const output = SaxonJS.serialize(doc, {method: "xml", indent: true, "omit-xml-declaration":true});
console.log(output);
})
</script>

Saxon-JS 安装客户端
Saxon-JS 下载页面

XML 格式化可以通过解析 Xml、在 DOM 树中添加或更改文本节点以进行缩进,然后将 DOM 序列化回 Xml 来完成。

请检查 < a href = “ https://jsonBrowser.sourceforge.io/format xml.js”rel = “ nofollow norefrer”> https://jsonbrowser.sourceforge.io/formatxml.js 中的 格式化 xml函数 您可以在 https://jsonbrowser.sourceforge.io/中看到该函数的运行情况 在 Xml 选项卡下。

下面是简化的代码。 Js 添加错误检查、可选删除注释、缩进作为参数并处理父节点之间的非空格文本。

const parser = new DOMParser();


const serializer = new XMLSerializer();


function formatXml(xml) {
let xmlDoc = parser.parseFromString(xml, 'application/xml');
let rootElement = xmlDoc.documentElement;
indentChildren(xmlDoc, rootElement, "\n", "\n  ");
xml = serializer.serializeToString(xmlDoc);
return xml;
}


function indentChildren(xmlDoc, node, prevPrefix, prefix) {
let children = node.childNodes;
let i;
let prevChild = null;
let prevChildType = 1;
let child = null;
let childType;
for (i = 0; i < children.length; i++) {
child = children[i];
childType = child.nodeType;
if (childType != 3) {
if (prevChildType == 3) {
// Update prev text node with correct indent
prevChild.nodeValue = prefix;
} else {
// Create and insert text node with correct indent
let textNode = xmlDoc.createTextNode(prefix);
node.insertBefore(textNode, child);
i++;
}
if (childType == 1) {
let isLeaf = child.childNodes.length == 0 || child.childNodes.length == 1 && child.childNodes[0].nodeType != 1;
if (!isLeaf) {
indentChildren(xmlDoc, child, prefix, prefix + "  ");
}
}
}
prevChild = child;
prevChildType =childType;
}
if (child != null) {
// Previous level indentation after last child
if (childType == 3) {
child.nodeValue = prevPrefix;
} else {
let textNode = xmlDoc.createTextNode(prevPrefix);
node.append(textNode);
}
}
}

参考资料: https://www.w3schools.com/XML/dom_intro.asp

这可能涉及到将节点创建为对象,但是您可以完全控制导出格式良好的 xml。

下面将返回一个行的字符串数组,您可以用一个新的行分隔符“ n”将这些行连接起来。

/**
* The child of an XML node can be raw text or another xml node.
*/
export type PossibleNode = XmlNode | string;


/**
* Base XML Node type.
*/
export interface XmlNode {
tag: string;
attrs?: { [key: string]: string };
children?: PossibleNode[];
}


/**
* Exports the given XML node to a string array.
*
* @param node XML Node
* @param autoClose Auto close the tag
* @param indent Indentation level
* @returns String array
*/
export function xmlNodeToString(
node: XmlNode,
autoClose: boolean = true,
indent: number = 0
): string[] {
const indentStr = " ".repeat(indent);
const sb: string[] = [];
sb.push(`${indentStr}<${node.tag}`);
if (node.attrs) {
for (const key in node.attrs) {
sb.push(`${indentStr} ${key}="${node.attrs[key]}"`);
}
}
if (node.children) {
if (node.children.length === 1 && typeof node.children[0] === "string") {
sb[sb.length - 1] += ">" + node.children[0];
} else {
sb.push(`${indentStr}>`);
for (const child of node.children) {
if (typeof child === "string") {
sb.push(`${indentStr}  ${child}`);
} else {
const lines = xmlNodeToString(child, autoClose, indent + 1);
sb.push(...lines.map((line) => `${indentStr}  ${line}`));
}
}
}
if (autoClose) {
if (node.children.length === 1 && typeof node.children[0] === "string") {
sb[sb.length - 1] += `</${node.tag}>`;
} else {
sb.push(`${indentStr}</${node.tag}>`);
}
}
} else {
if (autoClose) {
sb.push(`${indentStr}/>`);
} else {
sb.push(`${indentStr}>`);
}
}
return sb;
}


感谢更新要点: https://gist.github.com/rodydavis/acd609560ab0416b60681fddabc43eee