【问题标题】:XML - XSLT - for-each ordered by element positionXML - XSLT - for-each 按元素位置排序
【发布时间】:2018-07-11 17:08:15
【问题描述】:

我有以下 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 元素中获取属性,但根据它们在上方@987654325 中的顺序打印它们@ 元素。也许我很困惑,但这是我想要得到的结果 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>

请注意,oFieldnoField 元素的属性 name 将是相同的,但我不想从已排序的 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 元素排序,但由于其中有三个,/doc/order/oField[position()] 返回多个值并且无法进行排序(这就是注释该行的原因)

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

XSLT 中有没有办法做到这一点?

谢谢!

编辑/更新

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

谢谢!

亚历山大·哈辛托

【问题讨论】:

    标签: xml xslt xslt-3.0


    【解决方案1】:

    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

    【讨论】:

    • 这正是我一直在寻找的,因为我仍然想从 noField 元素的属性中获取值,但是按照它们在 oField 元素中出现的顺序。谢谢!
    【解决方案2】:

    我不确定我是否正确理解了您的问题。
    试试下面的模板,输出如你所愿。它使用noFieldposition() 作为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>
    ....
    

    【讨论】:

    • 谢谢!事实上,这会产生预期的结果。我将把它应用到我真正的问题上,如果它有效,我会告诉你的!
    • 问题是我仍然想从 元素的属性中获取数据,而不是从 元素中获取数据。我将把它应用到我真正的问题上,如果它有效,我会告诉你的!
    【解决方案3】:
    <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.
    

    【讨论】:

    • 感谢您的回答,但这基本上是按元素的字母顺序对元素进行排序,对吧?
    猜你喜欢
    • 2011-04-18
    • 2021-05-31
    • 1970-01-01
    • 1970-01-01
    • 2021-07-26
    • 1970-01-01
    • 2013-03-07
    • 2021-07-23
    • 2020-11-07
    相关资源
    最近更新 更多