【问题标题】:Sort parent nodes XML depends on child nodes values排序父节点 XML 取决于子节点值
【发布时间】:2011-09-12 07:12:14
【问题描述】:

我有以下 XML

<Root> 
  <Element A/>
  <Element B/>
  <Data1> 
    <DataElement/> 
    <Values>
       <Value>2222</Value> 
       <Name>field1</Name>
    </Values> 
    <Values>
       <Value>ABC</Value> 
       <Name>field2</Name>
    </Values> 
  </Data1> 
  <Data2> 
    <DataElement/> 
    <Values>
       <Value>1111</Value> 
       <Name>field1</Name>
    </Values> 
    <Values>
       <Value>XYZ</Value> 
       <Name>field2</Name>
    </Values> 
  </Data2>
  <DataN> 
    ... 
  </DataN> 
</Root> 

我需要获取相同的 XML,其中数据部分按指定字段名称的“值”排序,例如: 按“field1”排序将返回

    <Root> 
  <Element A/>
  <Element B/>
  <Data2> 
    <DataElement/> 
    <Values>
       <Value>1111</Value> 
       <Name>field1</Name>
    </Values> 
    <Values>
       <Value>XYZ</Value> 
       <Name>field2</Name>
    </Values> 
  </Data2>
  <Data1> 
    <DataElement/> 
    <Values>
       <Value>2222</Value> 
       <Name>field1</Name>
    </Values> 
    <Values>
       <Value>ABC</Value> 
       <Name>field2</Name>
    </Values> 
  </Data1>   
  <DataN> 
    ... 
  </DataN> 
</Root> 

另外,我必须将排序字段的名称作为参数发送...

【问题讨论】:

  • 提供的 XML 格式不正确。
  • 好问题,+1。保留DataX 元素和所有其他元素的相对顺序有点困难,但这绝对是可能的。

标签: xml xslt sorting xpath parameters


【解决方案1】:

这种转换完全实现了规定的要求。需要特别注意保留不排序的元素的确切顺序。目前没有其他答案可以这样做

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="vField" select="'field1'"/>
 <xsl:param name="pSortType" select="'number'"/>

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

   <xsl:template match="*[starts-with(name(),'Data')]">
    <xsl:variable name="vPos" select=
      "count(preceding-sibling::*[starts-with(name(),'Data')])+1"/>

  <xsl:for-each select="/*/*[starts-with(name(),'Data')]">
   <xsl:sort select="Values[Name=$vField]/Value"
             data-type="{$pSortType}"/>
   <xsl:if test="position() = $vPos">
    <xsl:copy-of select="."/>
   </xsl:if>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

应用于以下XML文档时(与提供的相同,但在Data1Data2之间插入了一个额外的&lt;Element C=""/&gt;,以便我们可以验证排序的保存未排序的元素):

<Root>
    <Element A=""/>
    <Element B=""/>
    <Data1>
        <DataElement/>
        <Values>
            <Value>2222</Value>
            <Name>field1</Name>
        </Values>
        <Values>
            <Value>ABC</Value>
            <Name>field2</Name>
        </Values>
    </Data1>
        <Element C=""/>
    <Data2>
        <DataElement/>
        <Values>
            <Value>1111</Value>
            <Name>field1</Name>
        </Values>
        <Values>
            <Value>XYZ</Value>
            <Name>field2</Name>
        </Values>
    </Data2>
</Root>

产生想要的正确结果——注意&lt;Element C=""/&gt;的位置被保留

<Root>
   <Element A=""/>
   <Element B=""/>
   <Data2>
      <DataElement/>
      <Values>
         <Value>1111</Value>
         <Name>field1</Name>
      </Values>
      <Values>
         <Value>XYZ</Value>
         <Name>field2</Name>
      </Values>
   </Data2>
   <Element C=""/>
   <Data1>
      <DataElement/>
      <Values>
         <Value>2222</Value>
         <Name>field1</Name>
      </Values>
      <Values>
         <Value>ABC</Value>
         <Name>field2</Name>
      </Values>
   </Data1>
</Root>

【讨论】:

    【解决方案2】:

    我认为你可以使用这个简单的转换:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        <xsl:strip-space elements="*"/>
    
        <xsl:param name="field" select="'field1'"/>
    
        <xsl:template match="@* | node()">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="Root">
            <xsl:copy>
                <xsl:apply-templates select="@*|*">
                    <xsl:sort select="Values[Name=$field]/
                        Value[string(number(.))!='NaN']" 
                        data-type="number"/>
                    <xsl:sort select="Values[Name=$field]/
                        Value[string(number(.))='NaN']"/>
                </xsl:apply-templates>
            </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    

    当应用于您的输入时(更正以使其格式正确):

    <Root> 
        <Element A=""/>
        <Element B=""/>
        <Data1> 
            <DataElement/> 
            <Values>
                <Value>2222</Value> 
                <Name>field1</Name>
            </Values> 
            <Values>
                <Value>ABC</Value> 
                <Name>field2</Name>
            </Values> 
        </Data1> 
        <Data2> 
            <DataElement/> 
            <Values>
                <Value>1111</Value> 
                <Name>field1</Name>
            </Values> 
            <Values>
                <Value>XYZ</Value> 
                <Name>field2</Name>
            </Values> 
        </Data2> 
    </Root>
    

    产生:

    <Root>
        <Element A=""></Element>
        <Element B=""></Element>
        <Data2>
            <DataElement></DataElement>
            <Values>
                <Value>1111</Value>
                <Name>field1</Name>
            </Values>
            <Values>
                <Value>XYZ</Value>
                <Name>field2</Name>
            </Values>
        </Data2>
        <Data1>
            <DataElement></DataElement>
            <Values>
                <Value>2222</Value>
                <Name>field1</Name>
            </Values>
            <Values>
                <Value>ABC</Value>
                <Name>field2</Name>
            </Values>
        </Data1>
    </Root>
    

    解释:

    • 使用身份规则按原样复制所有内容。
    • 覆盖所需的 DataN 元素并根据 $field 输入参数简单应用排序条件。
    • 用于排序的数据类型根据元素字段类型动态变化

    【讨论】:

    • 这种转换根本不会产生想要的输出! -1.
    • @Dimitre,很抱歉给您带来不便。我现在已经执行了正确的测试并为我的解决方案提供了修复。方法还是一样的。
    • @_empo:我明白了。在我的回答中尝试使用 XML 文档的解决方案,看看是否保留了元素顺序(提示:它不是)。
    • 感谢您的解释,我将元素名称从“Data1(2...N)”仅更改为“Data”,取消了作为参数的排序字段,并按 Data/Values/Value 排序。它对我来说已经足够了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    相关资源
    最近更新 更多