使用匹配特定选择器的 XSL 对 XML 进行排序并保持 XML 相同
Posted
技术标签:
【中文标题】使用匹配特定选择器的 XSL 对 XML 进行排序并保持 XML 相同【英文标题】:Sort XML with XSL matching specific selectors and keep XML the same 【发布时间】:2021-12-02 05:25:28 【问题描述】:我正在尝试使用 XSLT 1.0
对我的 XML 进行仅排序转换。除了 order/sequence 之外,我不需要对转换后的 XML 进行任何更改。
我创建了一个精简版的 XML,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<mpcconfiguration>
<lineitem id="0">
<seriesdesc>series1</seriesdesc>
<modeldesc>model1</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">3</property>
</option>
</category>
<category id="Category1">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">777</property>
</option>
</category>
</lineitem>
<lineitem id="1">
<seriesdesc>series2</seriesdesc>
<modeldesc>model2</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">1</property>
</option>
</category>
<category id="Category2">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">999</property>
</option>
</category>
</lineitem>
<lineitem id="2">
<seriesdesc>series3</seriesdesc>
<modeldesc>model3</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">2</property>
</option>
</category>
<category id="Category3">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">555</property>
</option>
</category>
</lineitem>
</mpcconfiguration>
以下是需要关注的重要方面:
-
根元素将始终为
mpcconfiguration
。
我需要在mpcconfiguration
下方对<lineitem>
元素进行相对排序。
排序序列应该由/mpcconfiguration/lineitem/category@id=Mstr_Information/option@id=Mstr_Information/property@id=Mstr_ModelSortOrder
的值驱动(该伪代码用简单的英语表示:“按<property>
的值排序,其id
是Mstr_ModelSortOrder
,其父级是@987654330 @ id 为Mstr_Information
,其父级为<category>
,id 为Mstr_Information
,其父级为<lineitem>
")
注意<property
元素的值,例如 555、777 和 999。出于排序目的,可以忽略这些元素,因为它们的祖先与我在 #3 中描述的模式不匹配。所有这些数据仍然必须在转换后的 XML 中,但这些与排序无关。
每个<lineitem>
将只有一个<property id="Mstr_ModelSortOrder">XXX</property>
,其祖先与上面#3 中描述的模式匹配。
如果我尝试解决的 XSL 行为正确,这是所需/转换后的 XML:
<?xml version="1.0" encoding="UTF-8"?>
<mpcconfiguration>
<lineitem id="1">
<seriesdesc>series2</seriesdesc>
<modeldesc>model2</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">1</property>
</option>
</category>
<category id="Category2">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">999</property>
</option>
</category>
</lineitem>
<lineitem id="2">
<seriesdesc>series3</seriesdesc>
<modeldesc>model3</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">2</property>
</option>
</category>
<category id="Category3">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">555</property>
</option>
</category>
</lineitem>
<lineitem id="0">
<seriesdesc>series1</seriesdesc>
<modeldesc>model1</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">3</property>
</option>
</category>
<category id="Category1">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">777</property>
</option>
</category>
</lineitem>
</mpcconfiguration>
请注意,这 2 个 xml 示例是相同的,只是 <lineitem>
节点的顺序不同,排序方式为:
<property id="Mstr_ModelSortOrder">1</property>
<property id="Mstr_ModelSortOrder">2</property>
<property id="Mstr_ModelSortOrder">3</property>
这是我对 xsl 的微弱尝试,虽然它不正确:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="no" />
<xsl:template match="/">
<xsl:copy-of select="*" />
</xsl:template>
<xsl:template match="mpcconfiguration">
<xsl:copy>
<xsl:apply-templates select="//mpcconfiguration/category/option/property">
<xsl:sort select="@id"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
我知道上面有相当多的 XML 和 XSL,但总结非常简单:按 Mstr_ModelSortOrder
XML <property>
对所有 <lineitem>
节点进行排序,只要该属性在 XML 中有正确的祖先树。
【问题讨论】:
【参考方案1】:这个 XSLT 1.0 转换符合您的描述
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="mpcconfiguration">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="lineitem">
<xsl:sort select="category[@id='Mstr_Information']/option[@id='Mstr_Information']/property[@id='Mstr_ModelSortOrder']" data-type="number" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
输出:
<?xml version="1.0" encoding="utf-8"?>
<mpcconfiguration>
<lineitem id="1">
<seriesdesc>series2</seriesdesc>
<modeldesc>model2</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">1</property>
</option>
</category>
<category id="Category2">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">999</property>
</option>
</category>
</lineitem>
<lineitem id="2">
<seriesdesc>series3</seriesdesc>
<modeldesc>model3</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">2</property>
</option>
</category>
<category id="Category3">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">555</property>
</option>
</category>
</lineitem>
<lineitem id="0">
<seriesdesc>series1</seriesdesc>
<modeldesc>model1</modeldesc>
<labels>
<label id="ExtPrice">Extended Price</label>
</labels>
<category id="Mstr_Information">
<description>Model Information</description>
<option id="Mstr_Information">
<description>descr1</description>
<unitprice>0</unitprice>
<property id="ExtPrice">0</property>
<property id="Mstr_ModelSortOrder">3</property>
</option>
</category>
<category id="Category1">
<description>a cool category</description>
<option id="option123">
<description>a cool option</description>
<unitprice>0</unitprice>
<property id="Mstr_ModelSortOrder">777</property>
</option>
</category>
</lineitem>
</mpcconfiguration>
模板#1 是身份模板。它匹配任何不匹配更具体模板的节点,并将其逐字复制到输出。
模板 #2 是唯一更具体的模板 - 它只匹配 <mpcconfiguration>
,复制它,并为任何属性节点 @*
调用匹配模板(您的输入样本中恰好没有)和任何<lineitem>
孩子,按他们各自的 <property id="Mstr_ModelSortOrder">
排序。这些节点的唯一匹配模板是身份模板,它会按原样完成工作并复制它们。
<xsl:strip-space elements="*" />
是为了方便,用<xsl:output indent="yes" />
实现漂亮的输出。
较短的版本假定 <mpcconfiguration>
是***元素,并使用 <xsl:for-each>
复制 <lineitem>
子元素:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/mpcconfiguration">
<xsl:copy>
<xsl:for-each select="lineitem">
<xsl:sort select="category[@id='Mstr_Information']/option[@id='Mstr_Information']/property[@id='Mstr_ModelSortOrder']" data-type="number" />
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
【讨论】:
Tomalak,感谢您的出色回答。效果很好。 Tomalak:我希望您可以在这里查看我的后续问题:***.com/questions/69563180/… - 这与原来的问题有很大不同,我认为最好提出一个新问题。提前致谢。以上是关于使用匹配特定选择器的 XSL 对 XML 进行排序并保持 XML 相同的主要内容,如果未能解决你的问题,请参考以下文章