java - 使用 XSL 样式表 Java Transformer Factory 对 XMLTag 进行排序
问题描述
我有以下 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/xslt}indent-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);
解决方案
在具有高阶函数支持的 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>
推荐阅读
- javascript - 如何从功能组件调用调度程序?
- android - 在启动、安装和重新启动时初始化应用程序?
- arrays - 扩展 Tableview 部分。检查部分是否扩展的问题
- c - 命令访问不存在的文件时弹出错误状态
- pyspark - 带有条件的 PySpark DataFrame 窗口分区
- google-cloud-platform - 实体的属性可以是 Google Cloud Datastore 中的另一个实体吗?
- google-sheets - 将一系列多列和多行列出到单个列中
- python - 如何修复“TypeError:无法将字典更新序列元素 #0 转换为序列”
- performance - 为什么这个简单的 Haskell 程序这么慢?
- powershell - 匹配两个不同文件中的字符串并输出此 + 以下行