XML-XSLT-每个元素按元素位置排序

时间:2018-07-11 17:08:15

标签: xml xslt xslt-3.0

我有以下XML文档:

<?xml version="1.0" encoding="utf-8" ?>
<doc>
    <order>
        <oField name="YEAR"></oField>
        <oField name="MONTH"></oField>
        <oField name="DAY"></oField>
    </order>
    <noOrder>
        <noField name="MONTH"></noField>
        <noField name="YEAR"></noField>
        <noField name="DAY"></noField>
    </noOrder>
</doc>

我想做的是创建一个新元素,即ordernoOrder的同级元素,该元素基本上从noField元素中获取属性,但根据它们的属性进行打印上order元素的顺序。也许我感到困惑,但这是我想要获得的XML文档结果:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
    <order>
        <oField name="YEAR"/>
        <oField name="MONTH"/>
        <oField name="DAY"/>
    </order>
    <noOrder>
        <noField name="MONTH"/>
        <noField name="YEAR"/>
        <noField name="DAY"/>
    </noOrder>
   <newOrder>
      <newOField>YEAR</newOField>
      <newOField>MONTH</newOField>
      <newOField>DAY</newOField>
   </newOrder>
</doc>

请注意,nameoField元素的属性noField都是相同的,但是我不想从oField元素中获取数据已订购。我想从noField元素中获取它,

这是我到目前为止拥有的XSLT代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0">

  <xsl:output indent="yes" cdata-section-elements="xml-property"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="doc/noOrder">
      <xsl:next-match/>
      <newOrder>
      <xsl:for-each select="noField">
          <!--<xsl:sort select="/doc/order/oField[position()]"/>-->
          <newOField>
              <xsl:value-of select="@name"/>
          </newOField>
      </xsl:for-each>
      </newOrder>
  </xsl:template>    

</xsl:stylesheet>

哪个会产生以下结果:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
    <order>
        <oField name="YEAR"/>
        <oField name="MONTH"/>
        <oField name="DAY"/>
    </order>
    <noOrder>
        <noField name="MONTH"/>
        <noField name="YEAR"/>
        <noField name="DAY"/>
    </noOrder>
   <newOrder>
      <newOField>MONTH</newOField>
      <newOField>YEAR</newOField>
      <newOField>DAY</newOField>
   </newOrder>
</doc>

正如您在XSLT代码中的注释行中所看到的那样,我尝试将xsl:sort元素设置为按oField元素进行排序,但是由于其中有三个,{{1} }返回多个值,并且无法进行排序(这就是为什么该行被注释的原因)

简单的解决方案是通过/doc/order/oField[position()]元素设置xsl:for-each,但是正如我之前所说,这不是我想要的,

XSLT中有一种方法可以做到这一点吗?

谢谢!

编辑/更新

也许我写问题的方式使它有些混乱,但是我想从元素而不是元素的属性中获取数据。但是由于它们相同(并且元素完全相同,具有相同的属性值),我想按它们在元素中出现的顺序(在元素中,元素内部)打印属性中的值(在元素中,元素内部)

谢谢!

Alexandre Jacinto

3 个答案:

答案 0 :(得分:0)

我不确定我是否正确理解了您的问题。
尝试以下模板,输出是所需的。它使用position()中的noField作为oField的索引,并打印其@name属性。

<xsl:template match="doc/noOrder">
    <xsl:next-match/>
    <newOrder>
      <xsl:for-each select="noField">
        <xsl:variable name="curPos" select="position()" />
        <newOField>
            <xsl:value-of select="/doc/order/oField[$curPos]/@name"/>
        </newOField>
      </xsl:for-each>
    </newOrder>
</xsl:template>   

输出为:

...
<newOrder>
  <newOField>YEAR</newOField>
  <newOField>MONTH</newOField>
  <newOField>DAY</newOField>
</newOrder>
....

答案 1 :(得分:0)

<xsl:output method="xml" indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="doc/noOrder">
        <xsl:next-match/>
        <newOrder>
        <xsl:for-each select="noField">
            <xsl:sort order="descending" select="@name"/>
            <newOField><xsl:value-of select="@name"/></newOField>
        </xsl:for-each>
        </newOrder>
    </xsl:template>
Try it.

答案 2 :(得分:0)

XPath中有一个index-of函数https://www.w3.org/TR/xpath-functions/#func-index-of,我认为您可以根据其他属性值的顺序来计算排序键:

  <xsl:template match="doc/noOrder">
      <xsl:next-match/>
      <newOrder>
      <xsl:variable name="sort-order" select="../order/oField/@name"/>
      <xsl:for-each select="noField">
          <xsl:sort select="index-of($sort-order, @name)"/>
          <newOField>
              <xsl:value-of select="@name"/>
          </newOField>
      </xsl:for-each>
      </newOrder>
  </xsl:template>

https://xsltfiddle.liberty-development.net/3NzcBtg