在开发Single Page Application时,我正在生成一些我希望能够在浏览器中阅读的XML。
在开发过程中,我通常在任何最新版本的Safari中工作,这一切都很有效,但是当我想向某人展示一些进展,并且他们使用的是Firefox时,生成的XML都在一条线上。 / p>
我根据我的研究选择了身份转换为漂亮的打印,因为它似乎是我问题的最干净的解决方案,但现在它似乎不适用于基于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中无效。有没有办法让这个功能跨浏览器,或者有更好的方法可以做到这一点吗?
(我希望在不添加其他库的情况下提供符合标准的解决方案)
答案 0 :(得分:1)
当您要求替代方案时,您与Dimitre Novatchev提出的讨论使用身份转换相关联的帖子也有一条评论建议使用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一样工作,因为我在Mozilla中首次引入的Javascript中仅使用了XSLTProcessor API,现在除了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的解决方案。