跨浏览器 xml 身份转换的漂亮打印替代方案
Cross browser xml pretty printing alternative to identity transform
在开发 Single Page Application 时,我正在生成一些 XML,我希望能够在浏览器中阅读。
在开发过程中,我通常使用任何最新版本的 Safari 并且一切正常,但是当我想向某人展示一些进展时,而他们使用的是 Firefox,生成的 XML 是全部在一条线上。
根据我所做的研究,我选择了身份转换为漂亮的打印,因为它似乎是解决我的问题的最干净的解决方案,但现在它似乎不适用于基于 Gecko 的浏览器。
我在 Typescript 中使用的代码是:
private prettifyXml(unformattedDocument:XMLDocument) : XMLDocument {
let identityTransformSheet = '\<' +
'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>';
let parser = new DOMParser();
let processor = new XSLTProcessor();
processor.importStylesheet(parser.parseFromString(identityTransformSheet, 'text/xml'));
let result = processor.transformToDocument(unformattedDocument);
return result;
}
而这个是用的:
// `xml` holds a document created by DOMParser and filled with a serialized representation of the model.
let result = (new XMLSerializer()).serializeToString(this.prettifyXml(xml));
this.xmlEquivalent(result);
return result;
这在 Safari 中运行良好,但在 Firefox 中似乎无能为力。有没有办法让这个功能跨浏览器,或者有更好的方法吗?
(我希望在不添加另一个库的情况下获得符合标准的解决方案)
当您询问替代方案时,您链接到讨论使用 Dimitre Novatchev 提出的身份转换的 post 也有一条评论建议使用 XPath 可视化工具样式表而不是身份转换。因此,为了证明这是可能的,我对 XSLT 以及 CSS 和 Javascript 进行了一些调整,以允许它在另一个 HTML 文档中使用,而不是创建一个单独的、完整的 HTML 文档,结果是 https://martin-honnen.github.io/js/2017/pretty-print/pretty-print-test1.html,它在 Windows 10 和当前版本的 Edge、Chrome 和 Firefox 上对我有用。我没有要测试的 Safari。它肯定不会像用 IE 编写的那样工作,因为我只使用了 Javascript 中的 XSLTProcessor API,最初是在 Mozilla 中引入的,现在除了 IE 之外的任何东西都支持;如果您需要 IE 支持,那么您应该能够通过使用 IE 特定代码来实现 运行 XSLT 转换。
这是HTML的代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Testing a proof of concept of pretty-printing of an XML DOM tree as a HTML collapsible table structure</title>
<link rel="stylesheet" type="text/css" href="pretty-print1.css"/>
<script>
var prettyPrinter = new XSLTProcessor();
(function() {
var req = new XMLHttpRequest();
req.open('GET', 'pretty-print1.xsl');
req.onload = function() {
prettyPrinter.importStylesheet(req.responseXML);
};
req.send();
}())
function prettyPrint(doc) {
return prettyPrinter.transformToFragment(doc, document);
}
function prettyPrintCollapseExpandHandler(event) {
try {
var thisNode = event.target;
var par = event.target.parentNode;
if (thisNode.nodeName == 'TD' && thisNode.className == 'expander') {
if (par.parentNode.className == 'expander-closed') {
par.parentNode.className = '';
thisNode.textContent = '-';
}
else {
par.parentNode.className = 'expander-closed';
thisNode.textContent = '+';
}
}
} catch (e) {
}
}
</script>
<script>
document.addEventListener('DOMContentLoaded',
function() {
var req = new XMLHttpRequest();
req.open('GET', 'input1.xml');
req.onload = function() {
document.getElementById('result').appendChild(prettyPrint(req.responseXML));
};
req.send();
},
false
);
</script>
</head>
<body>
<section>
<h1>Testing a proof of concept of pretty-printing of an XML DOM tree as a HTML collapsible table structure</h1>
<section id="result">
<h2>Example result</h2>
</section>
</section>
</body>
</html>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- The following is not used because of a bug in Mozilla :( -->
<!--
<xsl:key name="kattPref" match="@*"
use="concat(generate-id(..), '|', substring-before(., ':'))"/>
-->
<xsl:output method="html"/>
<xsl:template match="/">
<div class="pretty-print" onclick="prettyPrintCollapseExpandHandler(event);">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="*">
<div class="indent">
<span class="markup"><</span>
<xsl:variable name="class" select="'elemname'"/>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<xsl:call-template name="findNamespace"/>
<xsl:apply-templates select="@*"/>
<span class="markup">/></span>
</div>
</xsl:template>
<xsl:template match="*[text()]">
<xsl:variable name="class" select="'elemname'"/>
<div class="indent">
<span class="markup"><</span>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<xsl:call-template name="findNamespace"/>
<xsl:apply-templates select="@*"/>
<span class="markup">></span>
<!--<span class="text">
<xsl:value-of select="."/> -->
<xsl:apply-templates/>
<!--</span>-->
<span class="markup"></</span>
<span class="elemname">
<xsl:value-of select="name(.)"/>
</span>
<span class="markup">></span>
</div>
</xsl:template>
<xsl:template match="*[* or processing-instruction() or comment()
or string-length(text()) > 50]" priority="10">
<xsl:variable name="class" select="'elemname'"/>
<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td>
<span class="markup"><</span>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<xsl:call-template name="findNamespace"/>
<xsl:apply-templates select="@*"/>
<span class="markup">></span>
<div class="expander-content">
<xsl:apply-templates/>
</div>
<span class="markup"></</span>
<span class="elemname">
<xsl:value-of select="name(.)"/>
</span>
<span class="markup">></span>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="@*">
<xsl:variable name="vPos" select="position()"/>
<xsl:variable name="vPref" select="substring-before(name(), ':')"/>
<xsl:if test="$vPref
and
not(../@*[position() < $vPos]
[substring-before(name(), ':')
= $vPref]
)">
<xsl:call-template name="findNamespace"/>
</xsl:if>
<!-- The following is not used because of a bug in Mozilla :( -->
<!--
<xsl:if test=
"generate-id()
=
generate-id(key('kattPref',
concat(generate-id(..), '|', substring-before(., ':'))
)[1]
)">
<xsl:call-template name="findNamespace"/>
</xsl:if>
-->
<xsl:variable name="class" select="'attrname'"/>
<xsl:variable name="class2" select="'markup'"/>
<xsl:variable name="class3" select="'attrvalue'"/>
<xsl:text> </xsl:text>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<span class="{$class2}">="</span>
<span class="{$class3}">
<!-- <xsl:value-of select="."/> -->
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString" select="string(.)"/>
</xsl:call-template>
</span>
<span class="{$class2}">"</span>
</xsl:template>
<xsl:template match="text()">
<xsl:variable name="class" select="'text'"/>
<span class="{$class}">
<!-- <xsl:value-of select="."/> -->
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString" select="string(.)"/>
</xsl:call-template>
</span>
</xsl:template>
<xsl:template match="processing-instruction()">
<xsl:variable name="class" select="'indent pi'"/>
<div class="{$class}">
<?
<xsl:value-of select="name(.)"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
?>
</div>
</xsl:template>
<xsl:template match="processing-instruction()[string-length(.) > 50]">
<xsl:variable name="class" select="'pi'"/>
<xsl:variable name="class2" select="'indent expander-content'"/>
<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td class="{$class}">
<?
<xsl:value-of select="name(.)"/>
<div class="{$class2}">
<xsl:value-of select="."/>
</div>
<xsl:text>?></xsl:text>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="comment()">
<xsl:variable name="class" select="'comment indent'"/>
<div class="{$class}">
<!--
<xsl:value-of select="."/>
-->
</div>
</xsl:template>
<xsl:template match="comment()[string-length(.) > 50]">
<xsl:variable name="class" select="'comment'"/>
<xsl:variable name="class2" select="'indent expander-content'"/>
<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td class="{$class}">
<!--
<div class="{$class2}">
<xsl:value-of select="."/>
</div>
-->
</td>
</tr>
</table>
</xsl:template>
<xsl:template name="findNamespace">
<xsl:variable name="vName" select="substring-before(name(), ':')"/>
<xsl:variable name="vUri" select="namespace-uri(.)"/>
<xsl:variable name="vAncestNamespace">
<xsl:call-template name="findAncNamespace">
<xsl:with-param name="pName" select="$vName"/>
<xsl:with-param name="pUri" select="$vUri"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="not(number($vAncestNamespace))">
<xsl:if test="namespace-uri()
or
not(generate-id()
=
generate-id(../@*[name()
=
name(current())]
)
)">
<xsl:if test="parent::* or namespace-uri() or contains(name(), ':')">
<xsl:text> </xsl:text>
<span class="namespace">
<xsl:value-of select="'xmlns'"/>
<xsl:if test="contains(name(), ':')">
<xsl:value-of select="concat(':', $vName)"/>
</xsl:if>
</span>
<span class="markup">="</span>
<span class="namespace">
<xsl:value-of select="namespace-uri()"/>
</span>
<span class="markup">"</span>
</xsl:if>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template name="findAncNamespace">
<xsl:param name="pNode" select="."/>
<xsl:param name="pName" select="substring-before(name(), ':')"/>
<xsl:param name="pUri" select="namespace-uri(.)"/>
<xsl:choose>
<xsl:when test="not($pNode/parent::*)
and not($pName) and not($pUri)">1</xsl:when>
<xsl:when test="not($pNode/parent::*)">0</xsl:when>
<xsl:otherwise>
<xsl:variable name="vSamePrefs"
select="number($pName
= substring-before(name($pNode/..), ':')
)"/>
<xsl:variable name="vSameUris"
select="number($pUri = namespace-uri($pNode/..))"/>
<xsl:choose>
<xsl:when test="$vSamePrefs and not($vSameUris)">0</xsl:when>
<xsl:when test="not($vSamePrefs)">
<xsl:call-template name="findAncNamespace">
<xsl:with-param name="pNode" select="$pNode/.."/>
<xsl:with-param name="pName" select="$pName"/>
<xsl:with-param name="pUri" select="$pUri"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replaceAmpersands">
<xsl:param name="vString"/>
<xsl:variable name="vAmp">&</xsl:variable>
<xsl:choose>
<xsl:when test="contains($vString, $vAmp)">
<xsl:value-of select="substring-before($vString, $vAmp)"/>
<xsl:value-of select="concat($vAmp, 'amp;')"/>
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString"
select="substring-after($vString, $vAmp)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$vString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
拿 https://martin-honnen.github.io/js/2017/pretty-print/pretty-print1.css 中的 CSS 来说,无论如何它都是作为概念证明,而不是作为完善的完成代码。
这些天我宁愿建议像 https://github.com/pgfearo/xmlspectrum 这样的解决方案,它可以在 Saxon-CE 或 Saxon-JS 的帮助下在浏览器中运行。
在开发 Single Page Application 时,我正在生成一些 XML,我希望能够在浏览器中阅读。
在开发过程中,我通常使用任何最新版本的 Safari 并且一切正常,但是当我想向某人展示一些进展时,而他们使用的是 Firefox,生成的 XML 是全部在一条线上。
根据我所做的研究,我选择了身份转换为漂亮的打印,因为它似乎是解决我的问题的最干净的解决方案,但现在它似乎不适用于基于 Gecko 的浏览器。
我在 Typescript 中使用的代码是:
private prettifyXml(unformattedDocument:XMLDocument) : XMLDocument {
let identityTransformSheet = '\<' +
'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>';
let parser = new DOMParser();
let processor = new XSLTProcessor();
processor.importStylesheet(parser.parseFromString(identityTransformSheet, 'text/xml'));
let result = processor.transformToDocument(unformattedDocument);
return result;
}
而这个是用的:
// `xml` holds a document created by DOMParser and filled with a serialized representation of the model.
let result = (new XMLSerializer()).serializeToString(this.prettifyXml(xml));
this.xmlEquivalent(result);
return result;
这在 Safari 中运行良好,但在 Firefox 中似乎无能为力。有没有办法让这个功能跨浏览器,或者有更好的方法吗?
(我希望在不添加另一个库的情况下获得符合标准的解决方案)
当您询问替代方案时,您链接到讨论使用 Dimitre Novatchev 提出的身份转换的 post 也有一条评论建议使用 XPath 可视化工具样式表而不是身份转换。因此,为了证明这是可能的,我对 XSLT 以及 CSS 和 Javascript 进行了一些调整,以允许它在另一个 HTML 文档中使用,而不是创建一个单独的、完整的 HTML 文档,结果是 https://martin-honnen.github.io/js/2017/pretty-print/pretty-print-test1.html,它在 Windows 10 和当前版本的 Edge、Chrome 和 Firefox 上对我有用。我没有要测试的 Safari。它肯定不会像用 IE 编写的那样工作,因为我只使用了 Javascript 中的 XSLTProcessor API,最初是在 Mozilla 中引入的,现在除了 IE 之外的任何东西都支持;如果您需要 IE 支持,那么您应该能够通过使用 IE 特定代码来实现 运行 XSLT 转换。
这是HTML的代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Testing a proof of concept of pretty-printing of an XML DOM tree as a HTML collapsible table structure</title>
<link rel="stylesheet" type="text/css" href="pretty-print1.css"/>
<script>
var prettyPrinter = new XSLTProcessor();
(function() {
var req = new XMLHttpRequest();
req.open('GET', 'pretty-print1.xsl');
req.onload = function() {
prettyPrinter.importStylesheet(req.responseXML);
};
req.send();
}())
function prettyPrint(doc) {
return prettyPrinter.transformToFragment(doc, document);
}
function prettyPrintCollapseExpandHandler(event) {
try {
var thisNode = event.target;
var par = event.target.parentNode;
if (thisNode.nodeName == 'TD' && thisNode.className == 'expander') {
if (par.parentNode.className == 'expander-closed') {
par.parentNode.className = '';
thisNode.textContent = '-';
}
else {
par.parentNode.className = 'expander-closed';
thisNode.textContent = '+';
}
}
} catch (e) {
}
}
</script>
<script>
document.addEventListener('DOMContentLoaded',
function() {
var req = new XMLHttpRequest();
req.open('GET', 'input1.xml');
req.onload = function() {
document.getElementById('result').appendChild(prettyPrint(req.responseXML));
};
req.send();
},
false
);
</script>
</head>
<body>
<section>
<h1>Testing a proof of concept of pretty-printing of an XML DOM tree as a HTML collapsible table structure</h1>
<section id="result">
<h2>Example result</h2>
</section>
</section>
</body>
</html>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<!-- The following is not used because of a bug in Mozilla :( -->
<!--
<xsl:key name="kattPref" match="@*"
use="concat(generate-id(..), '|', substring-before(., ':'))"/>
-->
<xsl:output method="html"/>
<xsl:template match="/">
<div class="pretty-print" onclick="prettyPrintCollapseExpandHandler(event);">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="*">
<div class="indent">
<span class="markup"><</span>
<xsl:variable name="class" select="'elemname'"/>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<xsl:call-template name="findNamespace"/>
<xsl:apply-templates select="@*"/>
<span class="markup">/></span>
</div>
</xsl:template>
<xsl:template match="*[text()]">
<xsl:variable name="class" select="'elemname'"/>
<div class="indent">
<span class="markup"><</span>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<xsl:call-template name="findNamespace"/>
<xsl:apply-templates select="@*"/>
<span class="markup">></span>
<!--<span class="text">
<xsl:value-of select="."/> -->
<xsl:apply-templates/>
<!--</span>-->
<span class="markup"></</span>
<span class="elemname">
<xsl:value-of select="name(.)"/>
</span>
<span class="markup">></span>
</div>
</xsl:template>
<xsl:template match="*[* or processing-instruction() or comment()
or string-length(text()) > 50]" priority="10">
<xsl:variable name="class" select="'elemname'"/>
<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td>
<span class="markup"><</span>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<xsl:call-template name="findNamespace"/>
<xsl:apply-templates select="@*"/>
<span class="markup">></span>
<div class="expander-content">
<xsl:apply-templates/>
</div>
<span class="markup"></</span>
<span class="elemname">
<xsl:value-of select="name(.)"/>
</span>
<span class="markup">></span>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="@*">
<xsl:variable name="vPos" select="position()"/>
<xsl:variable name="vPref" select="substring-before(name(), ':')"/>
<xsl:if test="$vPref
and
not(../@*[position() < $vPos]
[substring-before(name(), ':')
= $vPref]
)">
<xsl:call-template name="findNamespace"/>
</xsl:if>
<!-- The following is not used because of a bug in Mozilla :( -->
<!--
<xsl:if test=
"generate-id()
=
generate-id(key('kattPref',
concat(generate-id(..), '|', substring-before(., ':'))
)[1]
)">
<xsl:call-template name="findNamespace"/>
</xsl:if>
-->
<xsl:variable name="class" select="'attrname'"/>
<xsl:variable name="class2" select="'markup'"/>
<xsl:variable name="class3" select="'attrvalue'"/>
<xsl:text> </xsl:text>
<span class="{$class}">
<xsl:value-of select="name(.)"/>
</span>
<span class="{$class2}">="</span>
<span class="{$class3}">
<!-- <xsl:value-of select="."/> -->
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString" select="string(.)"/>
</xsl:call-template>
</span>
<span class="{$class2}">"</span>
</xsl:template>
<xsl:template match="text()">
<xsl:variable name="class" select="'text'"/>
<span class="{$class}">
<!-- <xsl:value-of select="."/> -->
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString" select="string(.)"/>
</xsl:call-template>
</span>
</xsl:template>
<xsl:template match="processing-instruction()">
<xsl:variable name="class" select="'indent pi'"/>
<div class="{$class}">
<?
<xsl:value-of select="name(.)"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
?>
</div>
</xsl:template>
<xsl:template match="processing-instruction()[string-length(.) > 50]">
<xsl:variable name="class" select="'pi'"/>
<xsl:variable name="class2" select="'indent expander-content'"/>
<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td class="{$class}">
<?
<xsl:value-of select="name(.)"/>
<div class="{$class2}">
<xsl:value-of select="."/>
</div>
<xsl:text>?></xsl:text>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="comment()">
<xsl:variable name="class" select="'comment indent'"/>
<div class="{$class}">
<!--
<xsl:value-of select="."/>
-->
</div>
</xsl:template>
<xsl:template match="comment()[string-length(.) > 50]">
<xsl:variable name="class" select="'comment'"/>
<xsl:variable name="class2" select="'indent expander-content'"/>
<table>
<tr>
<td class="expander">
-
<div/>
</td>
<td class="{$class}">
<!--
<div class="{$class2}">
<xsl:value-of select="."/>
</div>
-->
</td>
</tr>
</table>
</xsl:template>
<xsl:template name="findNamespace">
<xsl:variable name="vName" select="substring-before(name(), ':')"/>
<xsl:variable name="vUri" select="namespace-uri(.)"/>
<xsl:variable name="vAncestNamespace">
<xsl:call-template name="findAncNamespace">
<xsl:with-param name="pName" select="$vName"/>
<xsl:with-param name="pUri" select="$vUri"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="not(number($vAncestNamespace))">
<xsl:if test="namespace-uri()
or
not(generate-id()
=
generate-id(../@*[name()
=
name(current())]
)
)">
<xsl:if test="parent::* or namespace-uri() or contains(name(), ':')">
<xsl:text> </xsl:text>
<span class="namespace">
<xsl:value-of select="'xmlns'"/>
<xsl:if test="contains(name(), ':')">
<xsl:value-of select="concat(':', $vName)"/>
</xsl:if>
</span>
<span class="markup">="</span>
<span class="namespace">
<xsl:value-of select="namespace-uri()"/>
</span>
<span class="markup">"</span>
</xsl:if>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template name="findAncNamespace">
<xsl:param name="pNode" select="."/>
<xsl:param name="pName" select="substring-before(name(), ':')"/>
<xsl:param name="pUri" select="namespace-uri(.)"/>
<xsl:choose>
<xsl:when test="not($pNode/parent::*)
and not($pName) and not($pUri)">1</xsl:when>
<xsl:when test="not($pNode/parent::*)">0</xsl:when>
<xsl:otherwise>
<xsl:variable name="vSamePrefs"
select="number($pName
= substring-before(name($pNode/..), ':')
)"/>
<xsl:variable name="vSameUris"
select="number($pUri = namespace-uri($pNode/..))"/>
<xsl:choose>
<xsl:when test="$vSamePrefs and not($vSameUris)">0</xsl:when>
<xsl:when test="not($vSamePrefs)">
<xsl:call-template name="findAncNamespace">
<xsl:with-param name="pNode" select="$pNode/.."/>
<xsl:with-param name="pName" select="$pName"/>
<xsl:with-param name="pUri" select="$pUri"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="replaceAmpersands">
<xsl:param name="vString"/>
<xsl:variable name="vAmp">&</xsl:variable>
<xsl:choose>
<xsl:when test="contains($vString, $vAmp)">
<xsl:value-of select="substring-before($vString, $vAmp)"/>
<xsl:value-of select="concat($vAmp, 'amp;')"/>
<xsl:call-template name="replaceAmpersands">
<xsl:with-param name="vString"
select="substring-after($vString, $vAmp)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$vString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
拿 https://martin-honnen.github.io/js/2017/pretty-print/pretty-print1.css 中的 CSS 来说,无论如何它都是作为概念证明,而不是作为完善的完成代码。
这些天我宁愿建议像 https://github.com/pgfearo/xmlspectrum 这样的解决方案,它可以在 Saxon-CE 或 Saxon-JS 的帮助下在浏览器中运行。