使用XSLT增量循环选择

时间:2013-05-18 16:02:34

标签: xml xslt

我面临以下问题:

如果我有以下XML数据:

<Input>
  <Error>
    <Info> 
      <Code> 111 </Code> 
      <Value>Hello User </Value>
    </Info>
    <Info> 
      <Code>118</Code> 
      <Value>01</Value>
    </Info>
  </Error>
  <Error>
    <Info> 
      <Code> 111 </Code> 
      <Value>Bye User </Value>
    </Info>
    <Info> 
      <Code>118</Code> 
      <Value>01</Value>
    </Info>
  </Error>
  <Error>
    <Info> 
      <Code> 111 </Code> 
      <Value>Dead User </Value>
    </Info>
    <Info> 
      <Code>118</Code> 
      <Value>06</Value>
    </Info>
  </Error>
  <Error>
    <Info> 
      <Code> 111 </Code> 
      <Value>Killed User </Value>
    </Info>
    <Info> 
      <Code>118</Code> 
      <Value>08</Value>
    </Info>
  </Error>
</Input>

如何知道输出应该类似于

,我如何为上面的内容构建XSLT转换
<RecNum>"Value found inside VALUE[2]"</RecNum>
<Error-Description> "Value found inside Value[1]" </Error-Description>

请注意,并不总是我有相同的输出。我确信,每个Error元素中每次都有2个Info元素和2个Value元素。

然而,我不知道我在同一个Error中有多少Value[2]个元素,所以有时我有3个Error元素,所有元素都有2个Info标签,但这三个错误,他们的Info[2]/Value/text()是相同的。

因此上述输入的输出应该是这样的

<Errors>
  <Module>
    <RecNum>1 </RecNum>
    <Error-Description>Hello user </Error-Description>
    <Error-Description>By User </Error-Description>
  </Module>
  <Module>
    <RecNum>6 </RecNum>
    <Error-Description> Dead User </Error-Description>
  </Module>
  <Module>
    <RecNum>8 </RecNum>
    <Error-Description> Killed User </Error-Description>
  </Module>
</Errors>

请注意,我没有02,03,04,05,07,09等的值......

我只有01,06&amp;的值。 08有时可能会有所不同。如果你愿意,怎么办这样的逻辑?

3 个答案:

答案 0 :(得分:3)

我建议在精神上使用模板时尽量避免xsl:for-each支持真正的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kErrorByVal" match="Error" use="*[2]/Value"/>

 <xsl:template match="/*">
     <Errors>
       <xsl:apply-templates select=
       "*[generate-id()=generate-id(key('kErrorByVal',*[2]/Value)[1])]"/>
     </Errors>
 </xsl:template>

 <xsl:template match="Error">
  <Module>
   <RecNum><xsl:value-of select="*[2]/Value"/></RecNum>
   <xsl:apply-templates select="key('kErrorByVal',*[2]/Value)/*[1]/Value"/>
  </Module>
 </xsl:template>

 <xsl:template match="Value">
  <Error-Description><xsl:value-of select="."/></Error-Description>
 </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<Input>
  <Error>
    <Info>
      <Code> 111 </Code>
      <Value>Hello User </Value>
    </Info>
    <Info>
      <Code>118</Code>
      <Value>01</Value>
    </Info>
  </Error>
  <Error>
    <Info>
      <Code> 111 </Code>
      <Value>Bye User </Value>
    </Info>
    <Info>
      <Code>118</Code>
      <Value>01</Value>
    </Info>
  </Error>
  <Error>
    <Info>
      <Code> 111 </Code>
      <Value>Dead User </Value>
    </Info>
    <Info>
      <Code>118</Code>
      <Value>06</Value>
    </Info>
  </Error>
  <Error>
    <Info>
      <Code> 111 </Code>
      <Value>Killed User </Value>
    </Info>
    <Info>
      <Code>118</Code>
      <Value>08</Value>
    </Info>
  </Error>
</Input>

产生了想要的正确结果:

<Errors>
   <Module>
      <RecNum>01</RecNum>
      <Error-Description>Hello User </Error-Description>
      <Error-Description>Bye User </Error-Description>
   </Module>
   <Module>
      <RecNum>06</RecNum>
      <Error-Description>Dead User </Error-Description>
   </Module>
   <Module>
      <RecNum>08</RecNum>
      <Error-Description>Killed User </Error-Description>
   </Module>
</Errors>

答案 1 :(得分:0)

此样式表使用键来标识具有相同Error的所有Info[2]/Value个节点。这被称为 Muenchian 方法。

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

  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" indent="yes"/>

  <xsl:key name="error-by-code" match="Error" use="Info[2]/Value"/>

  <xsl:template match="/">
    <Errors>
      <xsl:apply-templates select="Input/Error"/>
    </Errors>
  </xsl:template>

  <xsl:template match="Error">
    <xsl:variable name="error-code" select="Info[2]/Value"/>
    <xsl:if test="generate-id() = generate-id(key('error-by-code', $error-code)[1])">
      <Module>
        <RecNum>
          <xsl:value-of select="Info[2]/Value"/>
        </RecNum>
        <xsl:for-each select="key('error-by-code', $error-code)">
          <Error-Description>
            <xsl:value-of select="Info[1]/Value"/>
          </Error-Description>
        </xsl:for-each>
      </Module>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

<强>输出

<?xml version="1.0" encoding="utf-8"?>
<Errors>
   <Module>
      <RecNum>01</RecNum>
      <Error-Description>Hello User </Error-Description>
      <Error-Description>Bye User </Error-Description>
   </Module>
   <Module>
      <RecNum>06</RecNum>
      <Error-Description>Dead User </Error-Description>
   </Module>
   <Module>
      <RecNum>08</RecNum>
      <Error-Description>Killed User </Error-Description>
   </Module>
</Errors>

答案 2 :(得分:0)

这是没有任何ifs和变量的相同方法。它会检查所有错误值并对其进行分组。第二个for-each仅在第二个Info标签中选择所需的字符串。

这种方法应该是解决问题的快捷方法

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>
    <xsl:key name="errors-by-id" match="Error" use="Info[2]/Value" />
        <xsl:template match="Input">
            <xsl:for-each select="Error[count(. | key('errors-by-id', Info[2]/Value)[1]) = 1]">
                <xsl:sort select="Info[2]/Value" />
                <Modul>
                    <RecNum>
                        <xsl:value-of select="Info[2]/Value" />
                    </RecNum>
                <xsl:for-each select="key('errors-by-id', Info[2]/Value)">
                    <Error-Description>
                        <xsl:value-of select="Info[1]/Value"/>
                    </Error-Description>
                </xsl:for-each>
                </Modul>
            </xsl:for-each>
        </xsl:template> 
    </xsl:stylesheet>