基于XML元素值的XSLT分组

时间:2012-09-17 21:18:18

标签: xml xslt xpath saxon

我的输入XML文件看起来像

<test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
      <segment id="ORC"/>
      <segment id="OBR"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
   </test-message>

在我上面的输入XML文件元素 id =“ORC”是可选的

我想基于元素将我的输入XML文件与 id =“ORC”或元素 id分组= “OBR”

对于上面的输入XML文件我想在元素段与id =“ORC”存在时得到以下结果

<message-group>
    <test-message>
          <segment id="MSH"/>
          <segment id="SFT"/>
          <segment id="PID"/>
          <segment id="NTE"/>
          <segment id="NK1"/>
          <segment id="PV1"/>
</test-message>
<test-message>
          <segment id="ORC"/>
          <segment id="OBR"/>
          <segment id="NTE"/>
          <segment id="OBX"/>
          <segment id="NTE"/>
          <segment id="SPM"/>
       </test-message>
</message-group>

对于上面的输入XML文件我希望在元素段与id =“ORC”不存在时具有以下结果

<message-group>
    <test-message>
          <segment id="MSH"/>
          <segment id="SFT"/>
          <segment id="PID"/>
          <segment id="NTE"/>
          <segment id="NK1"/>
          <segment id="PV1"/>
</test-message>
<test-message>
          <segment id="OBR"/>
          <segment id="NTE"/>
          <segment id="OBX"/>
          <segment id="NTE"/>
          <segment id="SPM"/>
       </test-message>
</message-group>

我是否可以使用XSLT(2.0)模板或函数来处理上述场景

注意:我正在使用XSLT 2.0和撒克逊语解析器

3 个答案:

答案 0 :(得分:1)

此转化

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

 <xsl:template match="/*">
  <message-group>
   <xsl:for-each-group select="*" group-starting-with=
   "segment[@id='ORC'][not(preceding-sibling::segment[1][@id='OBR'])]
  | segment[@id='OBR'][not(preceding-sibling::segment[1][@id='ORC'])]

   ">

      <test-message><xsl:sequence select="current-group()"/></test-message>
   </xsl:for-each-group>
  </message-group>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档时:

<test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
      <segment id="ORC"/>
      <segment id="OBR"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
</test-message>

会产生想要的正确结果:

<message-group>
   <test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
   </test-message>
   <test-message>
      <segment id="ORC"/>
      <segment id="OBR"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
   </test-message>
</message-group>

当对此XML文档应用相同的转换(上面)时('ORC'不存在):

<test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
      <segment id="OBR"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
</test-message>

再次生成想要的正确结果

<message-group>
   <test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
   </test-message>
   <test-message>
      <segment id="OBR"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
   </test-message>
</message-group>

当对此XML文档应用相同的转换时('OBR'不存在):

<test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
      <segment id="ORC"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
</test-message>

再次生成想要的正确结果

<message-group>
   <test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
   </test-message>
   <test-message>
      <segment id="ORC"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
   </test-message>
</message-group>

最后,当'ORC'和'OBR'都存在时,'OBR'先于'ORC'

<test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
      <segment id="OBR"/>
      <segment id="ORC"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
</test-message>

再次产生正确的,想要的结果

<message-group>
   <test-message>
      <segment id="MSH"/>
      <segment id="SFT"/>
      <segment id="PID"/>
      <segment id="NTE"/>
      <segment id="NK1"/>
      <segment id="PV1"/>
   </test-message>
   <test-message>
      <segment id="OBR"/>
      <segment id="ORC"/>
      <segment id="NTE"/>
      <segment id="OBX"/>
      <segment id="NTE"/>
      <segment id="SPM"/>
   </test-message>
</message-group>

答案 1 :(得分:0)

快速想到两种相当简单的方法。

(1)在test-message的模板中,包含两个test-message输出元素,每个元素都包含apply-templates指令。给apply-templates一个参数来区分第一次和第二次调用。

说,像(未经测试):

<xsl:template match="test-message">
  <test-message>
    <xsl:apply-templates>
      <xsl:with-param name="flag" select="1"/>
    </xsl:apply-templates>
  </test-message>
  <test-message>
    <xsl:apply-templates>
      <xsl:with-param name="flag" select="2"/>
    </xsl:apply-templates>
  </test-message>
</xsl:template>

segment的模板中,如果(a)$ flag = 1则写出元素的副本,并且此段或任何前一个兄弟段的id都不是OBR或{{1或者,如果(b)$ flag = 2,则此段或某个前一个兄弟段具有这样的ORC。像

这样的东西
id

(2)如上所述制作<xsl:template match="segment"> <xsl:param name="flag"/> <xsl:if test="( $flag = 1 and not(@id = ('ORC', 'OBR')) and not(preceding-sibling::segment [@id=('ORC','OBR')]) ) or ( $flag = 2 and ((@id = ('ORC', 'OBR')) or preceding-sibling::segment [@id=('ORC','OBR')] )"> <xsl:copy-of select="."/> </xsl:if> 模板,但在test-message的两次调用中添加select="./segment[1]"

然后让xsl:apply-templates的模板完成它的工作,然后立即重复它的兄弟姐妹。为了保持逻辑简单,我们区分了几种情况:首先,segment我们还没有看到OBR或ORC:复制当前元素并继续前进。

$flag=1

其次,<xsl:template match="segment"> <xsl:param name="flag"/> <xsl:choose> <xsl:when test="$flag=1 and not(@id=('OBR', 'ORC'))"> <xsl:copy-of select="."/> <xsl:if test="not(@id=('OBR','ORC'))"> <xsl:apply-templates select="following-sibling::*[1]"> <xsl:with-param name="flag" select="$flag"/> </xsl:apply-templates> </xsl:if> </xsl:when> 我们现在遇到OBR或ORC。不要复制当前元素并且不要继续;第一个$flag = 1元素现已完成。

test-message

第三, <xsl:when test="$flag=1 and @id=('OBR', 'ORC')"> <!--* do nothing, stop recursion *--> </xsl:when> 我们还没有遇到过OBR或ORC。继续。

$flag = 2

第四, <xsl:when test="$flag=2 and not(@id=('OBR', 'ORC'))"> <!--* don't copy yet, keep looking for OBR/ORC *--> <xsl:apply-templates select="following-sibling::*[1]"> <xsl:with-param name="flag" select="$flag"/> </xsl:apply-templates> </xsl:when> 我们现在遇到OBR或ORC。复制当前元素并继续;将标志切换到第三个值,这意味着我们在第二个$flag = 2元素中,我们已经看到了OBR或ORC:

test-message

最后,如果 <xsl:when test="$flag=2 and @id=('OBR', 'ORC')"> <xsl:copy-of select="."/> <xsl:apply-templates select="following-sibling::*[1]"> <xsl:with-param name="flag" select="3"/> </xsl:apply-templates> </xsl:when> ,我们只需复制当前元素并继续。

$flag = 3

答案 2 :(得分:0)

如果你总是将序列分成两组,那么我想我会这样做:

<xsl:variable name="split" select="segment[@id=('ORC', 'OBR')][1]"/>
<test-message>
  <xsl:copy-of select="$split/preceding-sibling::*"/>  
</test-message>
<test-message>
  <xsl:copy-of select="$split, $split/following-sibling::*"/>  
</test-message>