XSLT:使用输入 XML 中的唯一且已排序的日期创建新的 XML 节点
Posted
技术标签:
【中文标题】XSLT:使用输入 XML 中的唯一且已排序的日期创建新的 XML 节点【英文标题】:XSLT: Create new XML node with unique and sorted dates from input XML 【发布时间】:2021-10-30 20:53:45 【问题描述】:我已经为此搜索了各种其他帖子,但到目前为止还没有得到这个工作。 我需要在生成的 XML 中添加一个新节点,该节点应该包含输入 XML 中的所有“start_date”节点。结果节点应具有按升序排序的唯一日期。 这是我的 XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<record>
<id>378</id>
<A>
<Field1>378</Field1>
<AX>
<Field1>x</Field1>
<Field2>xx</Field2>
</AX>
<AY>
<Field1>yy</Field1>
</AY>
<B>
<end_date>9999-12-31</end_date>
<start_date>2019-03-27</start_date>
</B>
<B>
<end_date>9999-12-31</end_date>
<start_date>2019-03-27</start_date>
</B>
<C>
<start_date>2012-02-01</start_date>
<user_id>10005557</user_id>
<D>
<end_date>9999-12-31</end_date>
<start_date>2021-06-30</start_date>
</D>
</C>
</A>
</record>
<record>
<id>379</id>
<A>
<Field1>300</Field1>
<AX>
<Field1>x</Field1>
<Field2>xx</Field2>
</AX>
<AY>
<Field1>yy</Field1>
</AY>
<B>
<end_date>9999-12-31</end_date>
<start_date>2019-03-27</start_date>
</B>
<C>
<start_date>2012-02-01</start_date>
<user_id>10005557</user_id>
<D>
<end_date>9999-12-31</end_date>
<start_date>2021-06-30</start_date>
</D>
</C>
</A>
</record>
</root>
这是我的 XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<root>
<xsl:for-each select="//record">
<record>
<xsl:copy-of select="./*"/>
<xsl:variable name="i" select="position()"/>
<StartDates>
<xsl:copy-of select="//child::record[$i]/descendant::start_date"/>
</StartDates>
</record>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template match="StartDates">
<xsl:variable name="nDate" select="replace(//start_date,'-','')"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="$nDate">
<xsl:sort select="$nDate"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
第一个模板是收集所有日期。在第二个模板中,我试图对这些日期进行排序。谁能帮我排序和选择独特的日期。
这是预期的输出:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<record>
<id>378</id>
<A>
<Field1>378</Field1>
<AX>
<Field1>x</Field1>
<Field2>xx</Field2>
</AX>
<AY>
<Field1>yy</Field1>
</AY>
<B>
<end_date>9999-12-31</end_date>
<start_date>2019-03-27</start_date>
</B>
<B>
<end_date>9999-12-31</end_date>
<start_date>2019-03-27</start_date>
</B>
<C>
<start_date>2012-02-01</start_date>
<user_id>10005557</user_id>
<D>
<end_date>9999-12-31</end_date>
<start_date>2021-06-30</start_date>
</D>
</C>
</A>
<StartDates>
<start_date>2012-02-01</start_date>
<start_date>2019-03-27</start_date>
<start_date>2021-06-30</start_date>
</StartDates>
</record>
<record>
<id>379</id>
<A>
<Field1>300</Field1>
<AX>
<Field1>x</Field1>
<Field2>xx</Field2>
</AX>
<AY>
<Field1>yy</Field1>
</AY>
<B>
<end_date>9999-12-31</end_date>
<start_date>2019-03-27</start_date>
</B>
<C>
<start_date>2012-02-01</start_date>
<user_id>10005557</user_id>
<D>
<end_date>9999-12-31</end_date>
<start_date>2021-06-30</start_date>
</D>
</C>
</A>
<StartDates>
<start_date>2012-02-01</start_date>
<start_date>2019-03-27</start_date>
<start_date>2021-06-30</start_date>
</StartDates>
</record>
</root>
感谢任何帮助!
【问题讨论】:
【参考方案1】:编写模板
<xsl:template match="record">
<xsl:copy>
<xsl:apply-templates/>
<StartDates>
<xsl:for-each-group select=".//start_date" group-by=".">
<xsl:sort select="."/>
<xsl:copy-of select="."/>
</xsl:for-each-group>
</StartDates>
</xsl:copy>
</xsl:template>
通过恒等转换处理其余部分(即声明 <xsl:mode on-no-match="shallow-copy"/>
或拼出 XSLT 2 的恒等转换模板:
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
)。
【讨论】:
这就像一个魅力。非常感谢!您能否简要解释一下这是如何选择唯一日期的。当我尝试使用 for-each 时,它会将所有日期都放在一个上下文中。我是 XSLT 和学习新手。再次感谢!<xsl:for-each-group select=".//start_date" group-by=".">
将start_date
元素按其字符串值分组,内部的xsl:sort
进行排序,因为<xsl:copy-of select="."/>
与<xsl:copy-of select="current-group()[1]"/>
相同,即输出第一个每个组的项,仅输出唯一的start_date
元素。以上是关于XSLT:使用输入 XML 中的唯一且已排序的日期创建新的 XML 节点的主要内容,如果未能解决你的问题,请参考以下文章
使用 XSLT 从同一输入 XML 中提取的最新日期更新输入 XML