XSLT:根据其他元素的存在,添加一个具有可变数量的子元素的新节点

时间:2014-10-03 00:49:58

标签: xslt xslt-2.0

我正在尝试根据XML文档中其他元素的存在来添加具有可变数量的子元素的新节点。这是我开始的简化示例:

输入:

<Parent>
    <FirstChild>
        <List>  
        <!--There could be an unlimited number of different Fruit elements in this list-->
            <Fruit A="Apples"/>
            <Fruit B="Bananas"/>
            <!--...-->
        </List>
    </FirstChild>
</Parent>

我需要添加一个新节点(Parent / SecondChild),其子元素是根据 List 节点中的元素创建的。输出应该是这样的(减去我的评论!):

期望输出:

<Parent>
    <FirstChild>
        <List>
        <!--There could be an unlimited number of Fruit elements in this list-->
            <Fruit A="Apples"/>
            <Fruit B="Bananas"/>
            <!--...-->
        </List>
    </FirstChild>
    <SecondChild>
        <NewList>
        <!--A separate <NewFruit> element needs to be added below based on the existence of the <Fruit> elements in <List> above-->
            <NewFruit A="Apples"/>
            <NewFruit B="Bananas"/>
            <!--...-->
        </NewList>
    </SecondChild>
</Parent>

我知道如何使用如下转换添加 SecondChild 节点和子元素:

变换:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" exclude-result-prefixes="xs fn xdt">
<xsl:output method="xml" version="1.0" encoding="UTF-8" omit-xml-declaration="yes" indent="yes"/>

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

<xsl:template match="Parent">
    <Parent>
    <xsl:apply-templates select="@*|*"/>
        <SecondChild>
             <NewList>
             </NewList>
        </SecondChild>
    </Parent>
</xsl:template>
</xsl:stylesheet>

这给出了以下输出:

当前输出:

<Parent>
    <FirstChild>
        <List>
        <!--There could be an unlimited number of Fruit elements in this list-->
            <Fruit A="Apples"/>
            <Fruit B="Bananas"/>
            <!--...-->
        </List>
    </FirstChild>
    <SecondChild>
        <NewList>
        <!--HELP!-->
        </NewList>
    </SecondChild>
</Parent>

但我不知道如何根据 Fruit 元素的存在动态创建 NewFruit 子元素(我的HELP!评论)在 FirstChild 节点中。任何帮助都将非常感激 - 我只是不确定此时的方向。

更新

我的问题的回复适用于我提供的XML(谢谢!),但它不适用于我实际使用的XML(出于保密原因我无法粘贴)。下面的XML完全模仿了我正在使用的格式:

输入:

<Parent>
    <Level1>
        <Level2>
            <List>
            <!--There could be an unlimited number of different Fruit elements in this list-->
                <Fruit A="Apples"/>
                <Fruit B="Bananas"/>
            </List>
        </Level2>
        <NewLevel2>
        </NewLevel2>
    </Level1>
</Parent>

我需要添加一个新节点(Parent / Level1 / NewLevel2 / NewNode),其中包含一些根据 List 节点中的元素创建的子元素。输出应如下所示:

期望输出:

<Parent>
    <Level1>
        <Level2>
            <List>
            <!--There could be an unlimited number of Fruit elements in this list-->
                <Fruit A="Apples"/>
                <Fruit B="Bananas"/>
            </List>
        </Level2>
        <NewLevel2>
            <NewNode>
                <NewChildNode>
                    <NewChildNode1/>
                    <NewList>
                    <!--A separate <NewFruit> element needs to be added below based on the existence of the <Fruit> elements in <List> above-->
                        <NewFruit A="Apples"/>
                        <NewFruit B="Bananas"/>
                    </NewList>
                </NewChildNode>
            </NewNode>
        </NewLevel2>
    </Level1>
</Parent>

这是我正在使用的XSLT:

变换:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" exclude-result-prefixes="xs fn xdt">
<xsl:output method="xml" version="1.0" encoding="UTF-8" omit-xml-declaration="yes" indent="yes"/>

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

<xsl:template match="NewLevel2">
    <NewLevel2>
        <xsl:apply-templates select="@*|*"/>
        <NewNode>
            <NewChildNode>
                <NewChildNode1/>
                <NewList>
                    <xsl:for-each select="*/List/Fruit">
                        <NewFruit>
                            <xsl:apply-templates select="@*|node()"/>
                        </NewFruit>
                    </xsl:for-each>
                </NewList>
            </NewChildNode>
        </NewNode>
    </NewLevel2>
</xsl:template>
</xsl:stylesheet>

这给出了以下输出:

当前输出:

<Parent>
    <Level1>
        <Level2>
            <List>
            <!--There could be an unlimited number of different Fruit elements in this list-->
                <Fruit A="Apples"/>
                <Fruit B="Bananas"/>
            </List>
        </Level2>
        <NewLevel2>
            <NewNode>
                <NewChildNode>
                    <NewChildNode1/>
                    <NewList/>
                </NewChildNode>
            </NewNode>
        </NewLevel2>
    </Level1>
</Parent>

因此NewList元素为空 - 由于某种原因未创建NewFruit子节点。但是,我不明白为什么当我发布的原始XML确实正确转换时这不起作用?

1 个答案:

答案 0 :(得分:1)

您可以按如下方式调整XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     xmlns:xs="http://www.w3.org/2001/XMLSchema" 
     xmlns:fn="http://www.w3.org/2005/xpath-functions" 
     xmlns:xdt="http://www.w3.org/2005/xpath-datatypes"
     exclude-result-prefixes="xs fn xdt">
<xsl:output method="xml" version="1.0" encoding="UTF-8" 
     omit-xml-declaration="yes" indent="yes"/>

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

<xsl:template match="Parent">
    <Parent>
    <xsl:apply-templates select="@*|*"/>
        <SecondChild>
          <NewList>
            <xsl:for-each select="*/List/Fruit">
              <NewFruit>
                 <xsl:apply-templates select="@* | node()"/>
              </NewFruit>
            </xsl:for-each>
          </NewList>
        </SecondChild>
    </Parent>
</xsl:template>
</xsl:stylesheet>

结果:

<Parent>
   <FirstChild>
      <List>  
         <Fruit A="Apples"/>
         <Fruit B="Bananas"/>
         <!--...-->
      </List>
   </FirstChild>
   <SecondChild>
      <NewList>
         <NewFruit A="Apples"/>
         <NewFruit B="Bananas"/>
      </NewList>
   </SecondChild>
</Parent>

更新:当您更新问题(输入XML以及所需的输出)时,以下调整后的XSLT会提供新的所需输出:

<xsl:stylesheet version="2.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
   xmlns:xs="http://www.w3.org/2001/XMLSchema" 
   xmlns:fn="http://www.w3.org/2005/xpath-functions" 
   xmlns:xdt="http://www.w3.org/2005/xpath-datatypes" 
   exclude-result-prefixes="xs fn xdt">
<xsl:output method="xml" version="1.0" encoding="UTF-8" 
     omit-xml-declaration="yes" indent="yes"/>

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

<xsl:template match="Parent">
<Parent>
    <xsl:apply-templates select="@*|*"/>
</Parent>
</xsl:template>

<xsl:template match="NewLevel2">
<NewLevel2>
  <NewNode>
    <NewChildNode>
       <NewChildNode1/>
         <NewList>
            <xsl:for-each select="//Parent//List/Fruit">
               <NewFruit>
                  <xsl:apply-templates select="@*|node()"/>
               </NewFruit>
            </xsl:for-each>
          </NewList>
     </NewChildNode>
  </NewNode>
</NewLevel2>
</xsl:template>
</xsl:stylesheet>

XML输入:

<Parent>
  <Level1>
    <Level2>
        <List>
            <Fruit A="Apples"/>
            <Fruit B="Bananas"/>
        </List>
    </Level2>
    <NewLevel2>
    </NewLevel2>
</Level1>
</Parent>

XML输出:

<Parent>
   <Level1>
        <Level2>
            <List>
                <Fruit A="Apples"/>
                <Fruit B="Bananas"/>
            </List>
        </Level2>
        <NewLevel2>
         <NewNode>
            <NewChildNode>
               <NewChildNode1/>
               <NewList>
                  <NewFruit A="Apples"/>
                  <NewFruit B="Bananas"/>
               </NewList>
            </NewChildNode>
         </NewNode>
      </NewLevel2>
    </Level1>
</Parent>

正如michael.hor257k已经提到的那样,您的模板匹配NewLevel2不会生成任何输出,因为NewLevel2只是输入XML中的空元素。因此,要获得所需的输出,只需调整XSLT以使模板匹配为NewLevel2生成NewLevel2的输出的空<xsl:for-each select="//Parent//List/Fruit">,而不是此模板的当前节点(为空),但来自根目录的水果列表 - {{1}}。

相关问题