XSLT将子节点拆分为新节点

时间:2014-01-16 09:46:36

标签: xml xslt

我需要转换XML文件的结构,并认为XSLT可能是最好的解决方案(这就是它的正确用途吗?)我对XSLT有一些经验,但它有点限制,所以我需要一些指导。

情景如下;我有一个包含一个或多个记录的XML。每条记录都有一组字段,每个字段都有一组子字段。子字段的顺序很重要。需要做的是输入结构需要进行转换,以便每次出现的子字段代码A和K都应该在输出中产生一个新字段(新代码和子字段K重命名为A)字段A或K后面的字段,如下例所示。子字段K的数量是任意的,并且可能因记录而不同,xslt需要略微一般。

这是我的输入XML:

    <?xml version="1.0" encoding="UTF-8" ?>
    <record>
      <field code="123">
         <subfield code="A">Abc</subfield>
         <subfield code="B">De</subfield>
         <subfield code="K">Fgh</subfield>
         <subfield code="C">IJ</subfield>
         <subfield code="K">Klmn</subfield>
         <subfield code="D">OP</subfield>
     </field>
     <field>... more datafields... </field>
   </record>

所需的输出如下:

    <?xml version="1.0" encoding="UTF-8" ?>
    <record>
      <field code="124">
         <subfield code="A">Abc</subfield>
         <subfield code="B">De</subfield>   
     </field>
     <field code="124">
         <subfield code="A">Fgh</subfield>
         <subfield code="C">IJ</subfield>
     </field>
     <field code="124">
         <subfield code="A">Klmn</subfield>
         <subfield code="D">OP</subfield>
     </field>
     <field>... more datafields... </field>
   </record>

如果有人可以提供一些样本xslt,至少指出我正确的方向,我会非常感激。

编辑:只是为了澄清。 A和K子字段之间可以有任意数量的子字段。

2 个答案:

答案 0 :(得分:1)

假设您可以使用像Saxon 9或Altova或XmlPrime这样的XSLT 2.0处理器

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

<xsl:strip-space elements="*"/>

<xsl:output indent="yes"/>

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

<xsl:template match="field">
  <xsl:for-each-group select="subfield" group-starting-with="subfield[@code = ('A', 'K')]">
    <field code="124">
      <xsl:apply-templates select="current-group()"/>
    </field>
  </xsl:for-each-group>
</xsl:template>

<xsl:template match="subfield/@code[. = 'K']">
  <xsl:attribute name="{name()}" select="'A'"/>
</xsl:template>

</xsl:stylesheet>

转换

<?xml version="1.0" encoding="UTF-8" ?>
    <record>
      <field code="123">
         <subfield code="A">Abc</subfield>
         <subfield code="B">De</subfield>
         <subfield code="K">Fgh</subfield>
         <subfield code="C">IJ</subfield>
         <subfield code="K">Klmn</subfield>
         <subfield code="D">OP</subfield>
     </field>
     <field>... more datafields... </field>
   </record>

<record>
   <field code="124">
      <subfield code="A">Abc</subfield>
      <subfield code="B">De</subfield>
   </field>
   <field code="124">
      <subfield code="A">Fgh</subfield>
      <subfield code="C">IJ</subfield>
   </field>
   <field code="124">
      <subfield code="A">Klmn</subfield>
      <subfield code="D">OP</subfield>
   </field>
</record>

如果您需要XSLT 1.0解决方案,那么

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

<xsl:strip-space elements="*"/>

<xsl:output indent="yes"/>

<xsl:key name="sub" match="subfield[not(@code = 'A' or @code = 'K')]"
  use="generate-id(preceding-sibling::subfield[@code = 'A' or @code = 'K'][1])"/>

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

<xsl:template match="field">
  <xsl:apply-templates select="subfield[@code = 'A' or @code = 'K']" mode="group"/>
</xsl:template>

<xsl:template match="subfield[@code = 'A' or @code = 'K']" mode="group">
  <field code="124">
    <xsl:apply-templates select=". | key('sub', generate-id())"/>
  </field>
</xsl:template>

<xsl:template match="subfield/@code[. = 'K']">
  <xsl:attribute name="{name()}">A</xsl:attribute>
</xsl:template>

</xsl:stylesheet>

应该这样做。

答案 1 :(得分:0)

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

<xsl:template match="subfield">
    <field code="{../@code}">
        <xsl:copy-of select="." />
        <xsl:copy-of select="following-sibling::subfield[1]" />
    </field>
</xsl:template>

<xsl:template match="record">
    <xsl:apply-templates select="//subfield[position() mod 2 = 1]" />
</xsl:template>

<xsl:template match="/">
    <xsl:apply-templates select="record" />
</xsl:template>

</xsl:stylesheet>