XSLT - 在分隔符上爆炸节点

时间:2012-08-05 12:52:35

标签: xml xslt

我需要一个XSLT 1.0来分解分隔符上的XML节点并创建单个节点。 例如,如果分隔符是' _' XML是:

<root>
    <a_b_c>hello<a_b_c>
    <a_b_c1>world</a_b_c1>
</root>

结果XML应为:

<root>
    <a>
        <b>
            <c>hello</c>
            <c1>world</c1>
        </b>
    </a>
</root>

不包含分隔符的节点应按原样输出。 我真的需要在XSLT1.0中使用它 谢谢你的帮助。

1 个答案:

答案 0 :(得分:2)

此转化

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

  <xsl:key name="kSameNameAdj" match="*"
  use="concat(generate-id(..),
              '+',
              generate-id(preceding-sibling::*[not(name()=name(current()))][1])
              )"/>

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

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <xsl:apply-templates select="node()"/>
  </xsl:variable>

  <xsl:apply-templates select="ext:node-set($vrtfPass1)" mode="compress"/>
 </xsl:template>

 <xsl:template name="explode" match=
  "*[contains(name(),'_')
   and not(substring(name(),1,1)='_')
   and not(substring(name(), string-length(name()))='_')
    ]">
    <xsl:param name="pName" select="name()"/>
    <xsl:param name="pText" select="text()"/>

  <xsl:choose>
   <xsl:when test="not($pName)">
    <xsl:value-of select="$pText"/>
   </xsl:when>
   <xsl:otherwise>
      <xsl:element name="{substring-before(concat($pName, '_'), '_')}">
       <xsl:call-template name="explode">
         <xsl:with-param name="pName" select="substring-after($pName, '_')"/>
         <xsl:with-param name="pText" select="$pText"/>
       </xsl:call-template>
      </xsl:element>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

  <xsl:template match="/" name="fold" mode="compress">
  <xsl:param name="pDoc" select="/"/>

  <xsl:choose>
    <xsl:when test="not($pDoc//*[name()=name(following-sibling::*[1])])">
      <xsl:copy-of select="$pDoc"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="vrtfThisPass">
        <xsl:apply-templates select="$pDoc/*" mode="compress"/>
      </xsl:variable>

      <xsl:call-template name="fold">
        <xsl:with-param name="pDoc" select="ext:node-set($vrtfThisPass)"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="node()|@*" mode="compress">
   <xsl:copy>
     <xsl:apply-templates select="@*|node()[1]" mode="compress"/>
   </xsl:copy>
   <xsl:apply-templates select="following-sibling::node()[1]" mode="compress"/>
 </xsl:template>

 <xsl:template match="*[name()=name(following-sibling::*[1])]" mode="compress">
  <xsl:element name="{name()}">
    <xsl:apply-templates mode="compress" select=
    "key('kSameNameAdj',
          concat(generate-id(..),
                 '+',generate-id(preceding-sibling::*)
                 )
         )/node()"/>
  </xsl:element>
  <xsl:apply-templates mode="compress" select=
  "key('kSameNameAdj',
          concat(generate-id(..),
                 '+',generate-id(preceding-sibling::*)
                 )
         )
          [last()]/following-sibling::node()[1]
   "/>
 </xsl:template>
</xsl:stylesheet>

应用于以下XML文档(提供的文档,扩展为更具挑战性):

<root>
   <x>This is:</x>
     <a_b_c>hello</a_b_c>
     <a_b_c_d>my</a_b_c_d>
     <a_b_c1>wonderful</a_b_c1>
     <a_b_c>world</a_b_c>
     <a_b>!</a_b>
   <y>The End</y>
</root>

生成想要的正确结果

<root>
   <x>This is:</x>
   <a>
      <b>
         <c>hello<d>my</d>
         </c>
         <c1>wonderful</c1>
         <c>world</c>!</b>
   </a>
   <y>The End</y>
</root>

<强>解释

0.1。 这是一次多次转化。 第一遍将XML文档转换为:

<root>
   <x>This is:</x>
   <a>
      <b>
         <c>hello</c>
      </b>
   </a>
   <a>
      <b>
         <c>
            <d>my</d>
         </c>
      </b>
   </a>
   <a>
      <b>
         <c1>wonderful</c1>
      </b>
   </a>
   <a>
      <b>
         <c>world</c>
      </b>
   </a>
   <a>
      <b>!</b>
   </a>
   <y>The End</y>
</root>

0.2。 结果传递,每个都将具有相同名称的任何相邻元素组压缩为具有该名称的单个元素 - 然后递归处理结果,直到不再有多个相邻兄弟的任何组具有相同名称的元素。

0.3。 第一遍使用identity rule

0.4。 下一次传递使用“压缩”模式中的fine-grade identity模板