想要转换具有混合内容的XML

时间:2014-01-31 08:41:16

标签: xslt xslt-2.0

我的XML如下所示,我想使用XSLT 2.0将其转换为所提到的预期xml格式。 child1child2只是样本,在实际的XML中,可能有几个具有不同名称的节点。我是XSL的初学者

<?xml version="1.0" encoding="utf-8" ?>
<properties xmlns="http://www.sss.org">
My Parent level text 1 <b> i am bold</b> not bold
<child1>
text1 of first child
<child1val attribute1="testval">36-37</child1val>
text2 of child <sub> subscript</sub>
</child1>    
<child2>
text1 of first child2
<child2val attribute1="testval">444</child2val>
<child2val>555</child2val>
text2 of child2
</child2>
My Parent level text3 in  <b> i am bold</b>
</properties>

预期结果:

<properties xmlns="http://www.sss.org">
<text>
 My Parent level text 1 <b> i am bold</b> not bold
</text>
<child1>
<text> text1 of first child </text>
<child1val attribute1="testval">36-37</child1val>
<text> text2 of child <i> i m italic</i></text>
</child1>    
<child2>
<text> text1 of second child2</text>
<child2val attribute1="testval">444</child2val>
<child2val>555</child2val>
<text> text2 of child2</text>
</child2>
My Parent level text3 in  <b> i am bold</b>

1 个答案:

答案 0 :(得分:2)

缩进XML的方式无助于您提出问题的方式。例如,请考虑这个简化的XML样本。

<properties>
My Parent level text 1 <b> i am bold</b> not bold
<child1>
text1 of first child
</child1>    
</properties>

它的布局方式似乎意味着属性元素有两个子元素。但事实并非如此,它有四个孩子。更明确的缩进就像这样

<properties>
   My Parent level text 1 
   <b> i am bold</b> 
   not bold
   <child1>
      text1 of first child
   </child1>    
</properties>

看起来 text 元素只需要包围前三个子元素,而不是 child1 元素。目前尚不清楚为什么 b 会在 text 元素中包围,但 child1 却没有,但我猜是因为 b i (以及 sub ?)是'html'元素,但 child1 不是。

做出这个假设,您可以使用XSLT 2.0中的 xsl:for-each-group 命令以及 group-adjacent 属性来解决此问题。您正在对相邻节点进行分组(如果它们是文本节点),或 b i sub ,则命令将如下所示

<xsl:for-each-group select="node()" 
                    group-adjacent="boolean(self::b|self::i|self::sub|self::text())">

在此范围内,您可以检查 current-grouping-key()功能,以确定是否需要使用 text 元素

    <xsl:choose>
       <xsl:when test="current-grouping-key()">
          <text>
            <xsl:apply-templates select="current-group()" />
          </text>
       </xsl:when>
       <xsl:otherwise>
            <xsl:apply-templates select="current-group()" />
       </xsl:otherwise>
    </xsl:choose> 

此代码本身可能只需要为具有其他元素作为子元素的元素运行,而不是针对仅具有单个文本节点作为子元素的元素运行。这意味着它将存在于此匹配的模板中

<xsl:template match="*[*]">

其他节点将与XSLT identity template匹配并复制。

试试这个XSLT

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.sss.org" xmlns="http://www.sss.org">

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

  <xsl:template match="*[*]">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:for-each-group select="node()" group-adjacent="boolean(self::b|self::i|self::sub|self::text())">
        <xsl:choose>
           <xsl:when test="current-grouping-key()">
              <text>
                <xsl:apply-templates select="current-group()" />
              </text>
           </xsl:when>
           <xsl:otherwise>
                <xsl:apply-templates select="current-group()" />
           </xsl:otherwise>
        </xsl:choose> 
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

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

(注意这里使用命名空间,因为XML中的所有元素都在命名空间中)。

这应输出以下内容

<properties xmlns="http://www.sss.org">
   <text>
      My Parent level text 1 <b> i am bold</b> not bold
   </text>
   <child1>
      <text>
        text1 of first child
      </text>
      <child1val attribute1="testval">36-37</child1val>
      <text>
         text2 of child <sub> subscript</sub>
      </text>
   </child1>
   <child2>
      <text>
        text1 of first child2
      </text>
      <child2val attribute1="testval">444</child2val>
      <child2val>555</child2val>
      <text>
         text2 of child2
      </text>
   </child2>
   <text>
      My Parent level text3 in  <b> i am bold</b>
   </text>
</properties>