使用 XSL 样式表 Java Transformer Factory 对 XMLTag 进行排序
Posted
技术标签:
【中文标题】使用 XSL 样式表 Java Transformer Factory 对 XMLTag 进行排序【英文标题】:Sorting XMLTag using XSL Stylesheet Java Transformer Factory 【发布时间】:2021-06-10 13:50:52 【问题描述】:我有下面的 XML 文件,我想以特定方式对其进行排序。基本上,XMLTags 将首先按字母顺序排序,然后在每个 XMLtag 中,其中一个 XML 元素也将用于按字母顺序对它们进行排序。请查看当前的 XML 和我需要的最终结果
原始 XML
<?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<applicationVisibilities>
<application>Test</application>
<default>true</default>
<visible>true</visible>
</applicationVisibilities>
<classAccesses>
<apexClass>TestClass</apexClass>
<enabled>false</enabled>
</classAccesses>
<applicationVisibilities>
<application>Class</application>
<default>true</default>
<visible>false</visible>
</applicationVisibilities>
<classAccesses>
<apexClass>FooClass</apexClass>
<enabled>false</enabled>
</classAccesses>
<fieldPermissions>
<editable>false</editable>
<field>Hello</field>
<readable>true</readable>
</fieldPermissions>
<applicationVisibilities>
<application>Foo</application>
<default>true</default>
<visible>false</visible>
</applicationVisibilities>
<fieldPermissions>
<editable>false</editable>
<field>Blah</field>
<readable>true</readable>
</fieldPermissions>
</Profile>
最终输出
<?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata">
<applicationVisibilities>
<application>Class</application>
<default>true</default>
<visible>false</visible>
</applicationVisibilities>
<applicationVisibilities>
<application>Foo</application>
<default>true</default>
<visible>false</visible>
</applicationVisibilities>
<applicationVisibilities>
<application>Test</application>
<default>true</default>
<visible>true</visible>
</applicationVisibilities>
<classAccesses>
<apexClass>FooClass</apexClass>
<enabled>false</enabled>
</classAccesses>
<classAccesses>
<apexClass>TestClass</apexClass>
<enabled>false</enabled>
</classAccesses>
<fieldPermissions>
<editable>false</editable>
<field>Blah</field>
<readable>true</readable>
</fieldPermissions>
<fieldPermissions>
<editable>false</editable>
<field>Hello</field>
<readable>true</readable>
</fieldPermissions>
</Profile>
标签排序后,对于每个标签,我将使用特定元素对它们进行排序。示例:applicaitonVisibilities 标记将使用应用程序 xmlelement 值按字母顺序排序。对于 classAccesses,apexClass 将用于按字母顺序排序,最后对于 fieldPermissions,将使用字段元素进行排序。我正在使用当前的 XSL 样式表,但它不起作用。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="Profile/applicationVisibilities">
<xsl:copy>
<xsl:apply-templates select="application">
<xsl:sort select="node()" data-type="text" order="ascending" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我当前使用当前 XSL 的 java 代码
XSL TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer(new StreamSource(new File("profile.xsl")));
//transformerFactory.setAttribute("indent-number", 10);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("http://xml.apache.org/xsltindent-amount", "4");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File(originalFile));
transformer.transform(source, result);
【问题讨论】:
JAXP 是一个 JRE/JDK API,它一方面具有内置处理器仅支持 XSLT 1,但另一方面在 Java 世界中可以轻松地与 Saxon 10 或 9 一起使用以具有XSLT 2 或 3 支持。你能把例如Saxon 10 HE 在课堂路径上? 是的,我可以将 saxon 放在类路径中。请提供版本#。 Saxon HE 10.3 是当前版本。 Saxon 10 HE 在 Sourceforge sourceforge.net/projects/saxon/files/Saxon-HE/10/Java 或 Maven 上可用。 【参考方案1】:在具有高阶函数支持的 XSLT 3(Saxon 10 或更高版本,Saxon 9.8 和 9.9 PE 和 EE,Saxon-JS 2)中,您可以对节点名称使用分组,然后根据从名称映射到选择子元素的函数:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://soap.sforce.com/2006/04/metadata"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="sort-keys"
as="map(xs:QName, function(*))"
select="map
QName('http://soap.sforce.com/2006/04/metadata', 'applicationVisibilities') : function($el) $el/application ,
QName('http://soap.sforce.com/2006/04/metadata', 'classAccesses') : function($el) $el/apexClass ,
QName('http://soap.sforce.com/2006/04/metadata', 'fieldPermissions') : function($el) $el/field
"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:sort select="string(current-grouping-key())"/>
<xsl:apply-templates select="current-group()">
<xsl:sort select="$sort-keys(node-name())(.)"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
在早期版本中,您可以使用
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://soap.sforce.com/2006/04/metadata"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:sort select="current-grouping-key() => string()"/>
<xsl:apply-templates select="current-group()">
<xsl:sort select="if (. instance of element(applicationVisibilities))
then application
else if (. instance of element(classAccesses))
then apexClass
else if (. instance of element(fieldPermissions))
then field
else ()"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
一个(希望是)纯 XSLT 2 版本的
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://soap.sforce.com/2006/04/metadata"
exclude-result-prefixes="#all"
version="2.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:for-each-group select="*" group-by="node-name(.)">
<xsl:sort select="string(current-grouping-key())"/>
<xsl:apply-templates select="current-group()">
<xsl:sort select="if (. instance of element(applicationVisibilities))
then application
else if (. instance of element(classAccesses))
then apexClass
else if (. instance of element(fieldPermissions))
then field
else ()"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
【讨论】:
感谢发帖。我更改了代码以使用 SAXON transofrmer 工厂并添加了此 XSL 样式表,但我遇到了一堆错误。 TransformerFactorytransformerFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl",null);xsl:param 在 profile.xsl 的第 14 行第 18 列出错: XPST0003: XPath 语法错误在第 14 行的字符 0 map QName:节点构造函数表达式只允许在 XQuery 中使用,而不是在 XPath 错误中 xsl:param on line 14 column 18 of profile.xsl: @darkest,我无法解释该错误消息,除非它来自早于 XSLT 3 的 Saxon 版本,例如 Saxon 9.7 或更早版本。无论如何,我为基于元素名称的排序添加了一个不太灵活但更兼容的替代方案。 我正在使用 saxon 9.1.8 但仍然出现错误。我尝试使用您发布的早期版本,但出现了不同的错误:profile.xsl 的第 13 行第 65 列 xsl:for-each-group 处出错:XPST0017:node-name( ): 函数 node-name() 必须有 1 个参数错误在 xsl:sort on line 14 column 70 of profile.xsl: 我发布了 XSLT 3 代码,假设至少 Saxon 9.8。对于像 9.1 这样的旧版本,我认为只有 XSLT 2 支持您需要进行两项更改,即group-by="node-name(.)"
而不是 group-by="node-name()"
,然后是 select="string(current-grouping-key())"
而不是 select="current-grouping-key() => string()"
您需要删除xsl:mode
声明,而是保留您发布的代码中的第一个xsl:template
。以上是关于使用 XSL 样式表 Java Transformer Factory 对 XMLTag 进行排序的主要内容,如果未能解决你的问题,请参考以下文章