2013-02-17 60 views
0

我想知道如何使用xsl模板将我的xml文档转换为具有原始元素层次结构的另一个xml文档我还想为新生成的XML中的元素添加一些属性。如何使用xsl模板和递归构建一个新的xml文档?

我原来的XML文件看起来是这样的:

<shop> 
    <product> 
    <cookie ID="001"> 
    <price>2</price> 
    </cookie> 
    </product> 

    <product> 
    <bread ID="002"> 
    <price>5</price> 
    </bread> 
    </product> 

    <product> 
    <milk ID="003"> 
    <price>2</price> 
    </milk> 
    </product> 
</shop> 

我想这个转换为下面的XML:

<newXML> 
    <newElement> 
    <newElement ID="001"> 
    <newElement price="2"/> 
    </newElement> 
    </newElement> 

    <newElement> 
    <newElement ID="002"> 
    <newElement price="5"/> 
    </newElement> 
    </newElement> 

    <newElement> 
    <newElement ID="003"> 
    <newElement price="2"/> 
    </newElement> 
    </newElement> 
</newXML> 

什么是做到这一点的好办法?这可以通过模板递归来完成,还是有更好的方法?我一直在尝试使用以下逻辑:

  1. 使创建元素
  2. 阅读潮流元素ID的模板,如果它存在,并把它为newElement
  3. 如果当前元素有一个孩子,这个应用模板(某种递归)

尽管进行了很多尝试,我还是无法完成这项工作。您的帮助将不胜感激!

回答

2

为此,您将基于XSLT标识转换进行构建,该转换是XSLT中非常常见的设计模式。首先你只是有一个模板,复制的元素,准确地把它们作为-是]

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

结果,你最后要做的就是添加额外的模板来匹配您的元素,并添加代码,以将其重命名,或添加属性按要求。例如,要重命名根元素,您需要添加以下模板:

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

您不必在此处使用特定的元素名称。如果你想重命名和元件都具有ID属性相同的名称,你可以做这样的事情

<xsl:template match="product/*[@ID]"> 
    <newElement> 
     <xsl:apply-templates select="@*|node()"/> 
    </newElement> 
</xsl:template> 

而在你价格元素的情况下,如果你想创建一个基于属性对元素的文本内容,您可以添加一个模板,像这样:

<xsl:template match="price"> 
    <newElement price="{.}" /> 
</xsl:template> 

(注意,这里采用属性值模板在这里创建属性这通常是首选使用。 XSL:属性

试试这个XML对于初学者:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 

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

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

    <xsl:template match="product/*[@ID]"> 
     <newElement> 
     <xsl:apply-templates select="@*|node()"/> 
     </newElement> 
    </xsl:template> 

    <xsl:template match="price"> 
     <newElement price="{.}" /> 
    </xsl:template> 

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

此输出以下:

<newXML> 
    <newElement> 
     <newElement ID="001"> 
     <newElement price="2"/> 
     </newElement> 
    </newElement> 
    <newElement> 
     <newElement ID="002"> 
     <newElement price="5"/> 
     </newElement> 
    </newElement> 
    <newElement> 
     <newElement ID="003"> 
     <newElement price="2"/> 
     </newElement> 
    </newElement> 
</newXML> 
+1

非常感谢你的非常详细的解答。所有似乎都在工作,据我测试exept 我想这个问题与“product/* [@ ID]”有关。我不断收到以下错误:运行时错误:file transform.xsl第47行元素副本 属性节点必须在任何子节点之前添加到元素。无论如何,你的回答非常有价值。 – Coltrane 2013-02-17 14:28:09

0

听起来像你只是想转换文档,那么为什么不尝试的XQuery代替XSLT?它简单得多..

如果您想要从输入文件中输入一些内容,并输入for $variable in //xpath来重写输入文件与xpath表达式匹配的部分,您只需编写要创建的xml和{..}

因此,这将创建所需的XML文件:

<newXML>{ 
    for $prod in /shop/product/* return 
    <newElement> 
     <newElement>{ 
     $prod/@ID, 
     for $value in $prod/* return 
      <newElement price="{$value}"/> 
     }</newElement> 
    </newElement> 
}</newXML>