XSLT用多个元素替换一个元素

时间:2015-12-31 13:41:45

标签: xml xslt xslt-1.0

我想用多个元素替换XML(大)中的元素,如下所示:

原始XML:

<root>
  <cr>
   <id>1</id>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <release>B</release>
  </cr>
</root>

我希望输出为:

<root>
  <cr>
   <id>1</id>
   <release>Aa</release>
   <release>Ab</release>
   <release>Ad</release>
  </cr>
  <cr>
   <id>2</id>
   <release>Bd</release>
   <release>Be</release>
  </cr>
</root>

原则是,每当//释放[text()=&#39; A&#39;]时,只要有//释放[text()=&#39; B&#39;],用上面的两个替换元素等。如果发布文本()是&#34; C&#34;或&#34; D&#34;或其他值,它们保持相同的值。

我的尝试:

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

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 
<xsl:strip-space elements="*"/>

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

<xsl:template match="//cr/release/text()">
    <xsl:if test=".='A'">
        <xsl:value-of select="Aa"/>
    </xsl:if>
</xsl:template>
</xsl:stylesheet>

它适用于一个 - &gt;一个替代,但如何做多个?非常感谢,

2 个答案:

答案 0 :(得分:2)

以这种方式尝试:

XSLT 1.0

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

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

<xsl:template match="release[.='A']">
    <release>Aa</release>
    <release>Ab</release>
    <release>Ad</release>
</xsl:template>

<xsl:template match="release[.='B']">
    <release>Bd</release>
    <release>Be</release>
</xsl:template>

</xsl:stylesheet>

测试输入

<root>
  <cr>
   <id>1</id>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <release>B</release>
  </cr>  
  <cr>
   <id>3</id>
   <release>C</release>
  </cr>
</root>

<强>结果

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <cr>
      <id>1</id>
      <release>Aa</release>
      <release>Ab</release>
      <release>Ad</release>
   </cr>
   <cr>
      <id>2</id>
      <release>Bd</release>
      <release>Be</release>
   </cr>
   <cr>
      <id>3</id>
      <release>C</release>
   </cr>
</root>

答案 1 :(得分:2)

这是一个完全通用且简短的XSLT 1.0解决方案。它使用映射 xml文件,指定每个所需版本的替换:

(请在本答案的最后找到用XSLT 2.0编写的最终通用和简短的解决方案。)

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

 <xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>

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

  <xsl:template match="release[. = document('mapping.xml')/*/release/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
</xsl:stylesheet>

如果文件mapping.xml与转换(.xsl文件)位于同一目录中,则为:

<map>
    <release old="A">
        <release>Aa</release>
        <release>Ab</release>
        <release>Ad</release>
    </release>
    <release old="B">
        <release>Ba</release>
        <release>Bb</release>
        <release>Bd</release>
    </release>
</map>

然后在此XML文档上应用转换时

<root>
  <cr>
   <id>1</id>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <release>B</release>
  </cr>
  <cr>
   <id>3</id>
   <release>C</release>
  </cr>
  <cr>
   <id>4</id>
   <release>D</release>
  </cr>
</root>

产生了想要的正确结果

<root>
   <cr>
      <id>1</id>
      <release>Aa</release>
      <release>Ab</release>
      <release>Ad</release>
   </cr>
   <cr>
      <id>2</id>
      <release>Ba</release>
      <release>Bb</release>
      <release>Bd</release>
   </cr>
   <cr>
      <id>3</id>
      <release>C</release>
   </cr>
   <cr>
      <id>4</id>
      <release>D</release>
   </cr>
</root>

注意:许多不同名称的元素可能会被相同的转换所取代。

如果我们有此映射文件

<map>
    <release old="A">
        <release>Aa</release>
        <release>Ab</release>
        <release>Ad</release>
    </release>
    <release old="B">
        <release>Ba</release>
        <release>Bb</release>
        <release>Bd</release>
    </release>
    <history old="p">
        <history>Pp</history>
        <history>Pq</history>
        <history>Pr</history>
    </history>
</map>

和此源XML文档

<root>
  <cr>
   <id>1</id>
   <history>p</history>
   <release>A</release>
  </cr>
  <cr>
   <id>2</id>
   <history>q</history>
   <release>B</release>
  </cr>
  <cr>
   <id>3</id>
   <history>r</history>
   <release>C</release>
  </cr>
  <cr>
   <id>4</id>
   <history>t</history>
   <release>D</release>
  </cr>
</root>

然后进行此转换

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

 <xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>

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

  <xsl:template match="release[. = document('mapping.xml')/*/release/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
  <xsl:template match="history[. = document('mapping.xml')/*/history/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
</xsl:stylesheet>

应用于上述XML文档时,生成所需结果 - 其中映射和替换了多个不同名称的元素:

<root>
   <cr>
      <id>1</id>
      <history>Pp</history>
      <history>Pq</history>
      <history>Pr</history>
      <release>Aa</release>
      <release>Ab</release>
      <release>Ad</release>
   </cr>
   <cr>
      <id>2</id>
      <history>q</history>
      <release>Ba</release>
      <release>Bb</release>
      <release>Bd</release>
   </cr>
   <cr>
      <id>3</id>
      <history>r</history>
      <release>C</release>
   </cr>
   <cr>
      <id>4</id>
      <history>t</history>
      <release>D</release>
   </cr>
</root>

最后:可以在XSLT 2.0中编写更简短,更通用的转换

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

 <xsl:variable name="vMap" select="document('mapping.xml')/*/*"/>

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

  <xsl:template match=
   "*[. = $vMap[name() eq name(current())]/@old]">
    <xsl:copy-of select="$vMap[name() eq name(current()) and @old eq current()]/*"/>
  </xsl:template>
</xsl:stylesheet>

请注意:此转换中没有硬编码元素名称!

更大的优势:我们可以修改转换,因此我们可以将映射文档的URI作为调用转换的全局参数传递 - 因此我们可以进行单一的泛型转换适用于任何未知的预先映射。

只有这些是必需的更改

 <xsl:param name="pmapUrl" select="'file:///c:/temp/mapping.xml'"/>

 <xsl:variable name="vMap" select="document($pmapUrl)/*/*"/>

完整转换(全局参数pmapUrl可以并且通常由转化的调用者动态指定:

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

 <xsl:param name="pmapUrl" select="'file:///c:/temp/mapping.xml'"/>

 <xsl:variable name="vMap" select="document($pmapUrl)/*/*"/>

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

  <xsl:template match=
   "*[for $name in name() 
        return
          . = $vMap[name() eq $name]/@old]">
    <xsl:copy-of select="$vMap[@old = current()]/*"/>
  </xsl:template>
</xsl:stylesheet>