2011-11-29 133 views
4

我有一个xml文档,现在我想将它翻译成具有相同内容但不同元素顺序的另一个xml文档。重新排列包含子节点的xml节点by xslt

原始的XML文档,如:

<?xml version = "1.0" encoding = "UTF-8"?> 
<order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" > 
<ship> 
    <zipcode>78712</zipcode> 
    <street>1234 Main Street</street> 
    <country>CN</country>  
    <city>Beijing</city> 
</ship> 
<items>  
    <quantity>1</quantity>  
    <itemno>1234</itemno> 
</items>  
<items>  
    <quantity>3</quantity>  
    <itemno>1235</itemno>  
</items>  
<price>456</price> 
<customer>Tom Hill</customer>  
</order> 

预期输出XML文档,如:

<?xml version = "1.0" encoding = "UTF-8"?> 
<order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" > 
<customer>Tom Hill</customer>  
<ship> 
    <street>1234 Main Street</street> 
    <city>Beijing</city> 
    <zipcode>78712</zipcode> 
    <country>CN</country>  
</ship>  
<items>  
    <itemno>1234</itemno>  
    <quantity>1</quantity>  
</items>  
<items>  
    <itemno>1235</itemno>  
    <quantity>3</quantity>  
</items>  
<price>456</price> 
</order> 

我用下面的XSLT文档进行翻译。

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
<xsl:template match="/order"> 
<xsl:copy> 
    <xsl:copy-of select="customer" /> 
    <xsl:copy-of select="ship" > 
    <xsl:call-template name="TShip" /> 
    </xsl:copy-of> 
    <xsl:copy-of select="items"> 
    <xsl:call-template name="TItems" /> 
    </xsl:copy-of> 
<xsl:copy-of select="price" /> 
</xsl:copy> 
</xsl:template> 

<xsl:template name="TShip"> 
<xsl:copy> 
    <xsl:copy-of select="street" /> 
    <xsl:copy-of select="city" /> 
    <xsl:copy-of select="zipcode" /> 
    <xsl:copy-of select="country" /> 
</xsl:copy> 
</xsl:template> 

<xsl:template name="TItems"> 
<xsl:for-each select="items"> 
    <xsl:copy> 
    <xsl:copy-of select="itemno" /> 
    <xsl:copy-of select="quantity" /> 
    </xsl:copy> 
</xsl:for-each> 
</xsl:template> 

</xsl:stylesheet> 

但是,翻译结果不是我的预期。 翻译结果XML:

<?xml version = "1.0" encoding = "UTF-8"?> 
<order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" > 
<customer>Tom Hill</customer>  
<ship> 
    <zipcode>78712</zipcode> 
    <street>1234 Main Street</street> 
    <country>CN</country>  
    <city>Beijing</city>  
</ship>  
<items>  
    <quantity>1</quantity>  
    <itemno>1234</itemno>  
</items>  
<items>  
    <quantity>3</quantity>  
    <itemno>1235</itemno> 
</items>  
<price>456</price> 
</order> 

它只是取得了预期的顺序中的第一级节点。所有的子节点都保持原来的顺序。我如何按照我的预期制作所有节点的顺序?

回答

9

xsl:copy-of复制所有子节点,并且它的子节点不被评估。

因此,您的TShip和TItems模板从未被评估过。<xsl:copy-of select="ship">复制全部<ship>...</ship>

此修改你的模板将证明你的TShip和TItems模板不会被调用。

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
<xsl:template match="/order"> 
<xsl:copy> 
    <xsl:copy-of select="customer" /> 
    <xsl:copy-of select="ship"> 
    <xsl:call-template name="TShip" /> 
</xsl:copy-of> 
    <xsl:copy-of select="items"> 
    <xsl:call-template name="TItems" /> 
    </xsl:copy-of> 
<xsl:copy-of select="price" /> 
</xsl:copy> 
</xsl:template> 

<xsl:template name="TShip"> 
<xsl:copy> 
    <test>TShip called</test> 
    <xsl:copy-of select="street" /> 
    <xsl:copy-of select="city" /> 
    <xsl:copy-of select="zipcode" /> 
    <xsl:copy-of select="country" /> 
</xsl:copy> 
</xsl:template> 

<xsl:template name="TItems"> 
<xsl:for-each select="items"> 
    <xsl:copy> 
    <test>TItems called</test> 
    <xsl:copy-of select="itemno" /> 
    <xsl:copy-of select="quantity" /> 
    </xsl:copy> 
</xsl:for-each> 
</xsl:template> 

</xsl:stylesheet> 

注意输出确实包含<test>元件我加入。

你需要做什么,而不是是递归的隐含复制。通常xsl:copyxsl:copy-ofxsl:for-each是坏的XSL模板设计的标志 - 有哪些xsl:templatexsl:apply-template与身份转换不处理非常好几个问题。

这是我会怎么做:

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

    <xsl:output encoding="UTF-8" indent="yes" method="xml" /> 

    <xsl:template match="order"> 
     <xsl:copy> 
      <!-- copy all attributes; maybe you don't want this --> 
      <xsl:apply-templates select="@*" /> 
      <!-- copy some elements in a specific order --> 
      <xsl:apply-templates select="customer" /> 
      <xsl:apply-templates select="ship" /> 
      <xsl:apply-templates select="items" /> 
      <xsl:apply-templates select="price" /> 
      <!-- now copy any other children that we haven't explicitly reordered; again, possibly this is not what you want --> 
      <xsl:apply-templates select="*[not(self::customer or self::ship or self::items or self::price)]"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="ship"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*" /> 
      <xsl:apply-templates select="street" /> 
      <xsl:apply-templates select="city" /> 
      <xsl:apply-templates select="zipcode" /> 
      <xsl:apply-templates select="country" /> 
      <xsl:apply-templates select="*[not(self::street or self::city or self::zipcode or self::country)]"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="items"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*" /> 
      <xsl:apply-templates select="itemno" /> 
      <xsl:apply-templates select="quantity" /> 
      <xsl:apply-templates select="*[not(self::itemno or self::quantity)]"/> 
     </xsl:copy> 
    </xsl:template> 

    <!-- this is the identity transform: it copies everything that isn't matched by a more specific template --> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

注意少很多的假设如何模板设计,让你的源XML的结构。它也很容易改变:例如,如果你想沉默或重命名一个本身有孩子的特定元素,你只需添加一个新的匹配该元素的xsl:template,做任何你需要做的事情,然后xsl:apply-templates对孩子。

你应该learn more about this XSLT pattern,因为它是非常灵活的,使模板创作少得多乏味,你的模板更容易脆弱。

+0

这是完美的,谢谢你的指导。 – zgcharley

1

我如何使所有节点的顺序如我所料?

简短的回答:通过使用<xsl:apply-templates/><xsl:template>代替<xsl:copy-of>


这是一个完整的转型

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

<xsl:template match="order"> 
    <xsl:copy> 
    <xsl:apply-templates select="customer"/> 
    <xsl:apply-templates select="*[not(self::customer)]"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="ship"> 
    <xsl:copy> 
    <xsl:apply-templates select="street"/> 
    <xsl:apply-templates select="city"/> 
    <xsl:apply-templates select="zipcode"/> 
    <xsl:apply-templates select="country"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="items"> 
    <xsl:copy> 
    <xsl:apply-templates select="itemno"/> 
    <xsl:apply-templates select="quantity"/> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

当这种转变是在应用提供了XML文档

<order xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" > 
    <ship> 
     <zipcode>78712</zipcode> 
     <street>1234 Main Street</street> 
     <country>CN</country> 
     <city>Beijing</city> 
    </ship> 
    <items> 
     <quantity>1</quantity> 
     <itemno>1234</itemno> 
    </items> 
    <items> 
     <quantity>3</quantity> 
     <itemno>1235</itemno> 
    </items> 
    <price>456</price> 
    <customer>Tom Hill</customer> 
</order> 

想要的,正确的结果产生

<order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <customer>Tom Hill</customer> 
    <ship> 
     <street>1234 Main Street</street> 
     <city>Beijing</city> 
     <zipcode>78712</zipcode> 
     <country>CN</country> 
    </ship> 
    <items> 
     <itemno>1234</itemno> 
     <quantity>1</quantity> 
    </items> 
    <items> 
     <itemno>1235</itemno> 
     <quantity>3</quantity> 
    </items> 
    <price>456</price> 
</order> 

说明

<xsl:copy-of select="someElement"/> 

副本someElement根的整个子树正是因为是(如果我们有一条重新安排子孙的指令,这条指令如何知道我们想要的顺序?)。

为了改变任何兄弟姐妹的顺序 - 元素,我们必须指定新的想要的顺序。

这可以通过编写一个<xsl:apply-templates>指令序列来完成,每个指令都按照所需的顺序选择所需的元素。我们可以编写<xsl:copy-of>指令,但仅限于复制要素,其后代我们希望保持其原始顺序。

+0

感谢您的回复 – zgcharley

+0

@zgcharley:不客气。 –