【问题标题】:Sorting XML recursively - only the inner nodes are sorted递归地对 XML 进行排序 - 仅对内部节点进行排序
【发布时间】:2011-03-18 11:34:07
【问题描述】:

我需要对示例 XML 进行排序,首先对所有 ShippingPoints 进行排序,然后根据第一个 ShippingPoint 对 Cargos 进行排序,最后根据第一个 Cargo 中的第一个 ShippingPoint 对 Transports 进行排序。所以基本上我正在尝试根据它们应该开始的日期对所有传输进行排序。

现在,我找到了一个使用 XSL 递归的解决方案,除了只有 Cargos 和 ShippingPoints 按预期排序 - 最外部的 Transport 节点没有。我想知道我在这里做错了什么。 MSXML (VS2008) 和 Saxon 解析器都给了我完全相同的结果。

示例 XML 代码:

<?xml version="1.0" encoding="utf-8"?>
<Transports>
    <Transport ID="1893">
        <Cargos>
            <Cargo ID="1532" >
                <ShippingPoints>
                    <ShippingPoint ID="1600" ArrivesOn="2011-04-07T12:00:00" />
                    <ShippingPoint ID="1601" ArrivesOn="2011-04-08T12:00:00" />
                </ShippingPoints>
            </Cargo>
            <Cargo ID="1532">
                <ShippingPoints>
                    <ShippingPoint ID="1601" ArrivesOn="2011-03-08T12:00:00" />
                    <ShippingPoint ID="1600" ArrivesOn="2011-02-07T12:00:00" />
                </ShippingPoints>
            </Cargo>
        </Cargos>
    </Transport>

    <Transport ID="1891" >
        <Cargos>
            <Cargo ID="1529" >
                <ShippingPoints>
                    <ShippingPoint ID="1594" ArrivesOn="2011-04-14T12:00:00" />
                    <ShippingPoint ID="1595" ArrivesOn="2011-04-04T13:00:00" />
                </ShippingPoints>
            </Cargo>
            <Cargo ID="1530" >
                <ShippingPoints>
                    <ShippingPoint ID="1597" ArrivesOn="2011-04-09T18:00:00" />
                    <ShippingPoint ID="1596" ArrivesOn="2011-04-04T12:00:00" />
                </ShippingPoints>
            </Cargo>
        </Cargos>
    </Transport>

    <Transport ID="1892">
        <Description/>
        <Cargos>
            <Cargo ID="1531" >
                <ShippingPoints>
                    <ShippingPoint ID="1599" ArrivesOn="2011-04-06T18:00:00" />
                    <ShippingPoint ID="1598" ArrivesOn="2011-04-05T12:00:00" />
                </ShippingPoints>
            </Cargo>
            <Cargo ID="1531" >
                <ShippingPoints>
                    <ShippingPoint ID="1599" ArrivesOn="2011-04-02T18:00:00" />
                    <ShippingPoint ID="1598" ArrivesOn="2011-04-03T12:00:00" />
                </ShippingPoints>
            </Cargo>
        </Cargos>
    </Transport>
</Transports>

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">

    <xsl:output method="xml" version="1.0" encoding="UTF-8" />

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="ShippingPoints">
        <xsl:copy>
            <xsl:apply-templates select="ShippingPoint">
                <xsl:sort select="@ArrivesOn" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Cargos">
        <xsl:copy>
            <xsl:apply-templates select="Cargo">
                <xsl:sort select="ShippingPoints/ShippingPoint[1]/@ArrivesOn" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Transports">
        <xsl:copy>
            <xsl:apply-templates select="Transport">
                <xsl:sort select="Cargos/Cargo[1]/ShippingPoints/ShippingPoint[1]/@ArrivesOn"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

【问题讨论】:

    标签: xml recursion msxml xslt-2.0 saxon


    【解决方案1】:

    除非我不明白这一点,否则您正在寻找一个至少为@ArrivesOndescendant 的排序。最短的样式表:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:apply-templates select="node()">
                    <xsl:sort select="min(.//@ArrivesOn/xs:dateTime(.))"/>
                </xsl:apply-templates>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    输出:

    <Transports>
        <Transport ID="1893">
            <Cargos>
                <Cargo ID="1532">
                    <ShippingPoints>
                        <ShippingPoint ID="1600" ArrivesOn="2011-02-07T12:00:00"/>
                        <ShippingPoint ID="1601" ArrivesOn="2011-03-08T12:00:00"/>
                    </ShippingPoints>
                </Cargo>
                <Cargo ID="1532">
                    <ShippingPoints>
                        <ShippingPoint ID="1600" ArrivesOn="2011-04-07T12:00:00"/>
                        <ShippingPoint ID="1601" ArrivesOn="2011-04-08T12:00:00"/>
                    </ShippingPoints>
                </Cargo>
            </Cargos>
        </Transport>
        <Transport ID="1892">
            <Description/>
            <Cargos>
                <Cargo ID="1531">
                    <ShippingPoints>
                        <ShippingPoint ID="1599" ArrivesOn="2011-04-02T18:00:00"/>
                        <ShippingPoint ID="1598" ArrivesOn="2011-04-03T12:00:00"/>
                    </ShippingPoints>
                </Cargo>
                <Cargo ID="1531">
                    <ShippingPoints>
                        <ShippingPoint ID="1598" ArrivesOn="2011-04-05T12:00:00"/>
                        <ShippingPoint ID="1599" ArrivesOn="2011-04-06T18:00:00"/>
                    </ShippingPoints>
                </Cargo>
            </Cargos>
        </Transport>
        <Transport ID="1891">
            <Cargos>
                <Cargo ID="1530">
                    <ShippingPoints>
                        <ShippingPoint ID="1596" ArrivesOn="2011-04-04T12:00:00"/>
                        <ShippingPoint ID="1597" ArrivesOn="2011-04-09T18:00:00"/>
                    </ShippingPoints>
                </Cargo>
                <Cargo ID="1529">
                    <ShippingPoints>
                        <ShippingPoint ID="1595" ArrivesOn="2011-04-04T13:00:00"/>
                        <ShippingPoint ID="1594" ArrivesOn="2011-04-14T12:00:00"/>
                    </ShippingPoints>
                </Cargo>
            </Cargos>
        </Transport>
    </Transports>
    

    【讨论】:

      【解决方案2】:

      如果您想要逐步转换,那么您应该使用模式和变量来存储临时结果,如下面的 XSLT 2.0 示例:

      <?xml version="1.0" encoding="UTF-8"?>
      
      <xsl:stylesheet version="2.0"
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      
          <xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8" />
          <xsl:strip-space elements="*"/>
      
          <xsl:template match="/">
            <xsl:variable name="t1">
              <xsl:apply-templates mode="step1"/>
            </xsl:variable>
            <xsl:variable name="t2">
              <xsl:apply-templates select="$t1/node()" mode="step2"/>
            </xsl:variable>
            <xsl:apply-templates select="$t2/node()"/>
          </xsl:template>
      
          <xsl:template match="@*|node()" mode="#all">
              <xsl:copy>
                  <xsl:apply-templates select="@*, node()" mode="#current"/>
              </xsl:copy>
          </xsl:template>
      
          <xsl:template match="ShippingPoints" mode="step1">
              <xsl:copy>
                  <xsl:apply-templates select="ShippingPoint">
                      <xsl:sort select="@ArrivesOn" />
                  </xsl:apply-templates>
              </xsl:copy>
          </xsl:template>
      
          <xsl:template match="Cargos" mode="step2">
              <xsl:copy>
                  <xsl:apply-templates select="Cargo">
                      <xsl:sort select="ShippingPoints/ShippingPoint[1]/@ArrivesOn" />
                  </xsl:apply-templates>
              </xsl:copy>
          </xsl:template>
      
          <xsl:template match="Transports">
              <xsl:copy>
                  <xsl:apply-templates select="Transport">
                      <xsl:sort select="Cargos/Cargo[1]/ShippingPoints/ShippingPoint[1]/@ArrivesOn"/>
                  </xsl:apply-templates>
              </xsl:copy>
          </xsl:template>
      </xsl:stylesheet>
      

      我认为 XSLT 2.0 处理器可以满足您的需求。

      【讨论】:

      • 谢谢@martin。这在这里也确实有效,但由于我真的不需要逐步执行转换,所以我按照@alejandro 的建议进行了。
      猜你喜欢
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      • 2023-01-11
      • 2017-01-01
      • 1970-01-01
      • 2018-01-16
      • 2023-02-01
      • 1970-01-01
      相关资源
      最近更新 更多