通过 xslt 重新排列 xml 节点,包括子节点 [英] rearrange xml nodes including sub-nodes by xslt

查看:41
本文介绍了通过 xslt 重新排列 xml 节点,包括子节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 xml 文档,现在我想将其转换为另一个内容相同但元素顺序不同的 xml 文档.

I have a xml document, now i want to translate it to another xml document with same content but different element orders.

原始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 文档如:

The expected output xml document like:

<?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 文档来翻译它.

I used following xslt document to translate it.

<?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:

However, the translated result is not my expected. Translated result 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>  

它只是按照预期的顺序制作了第一级节点.所有子节点都保持原始顺序.我怎样才能按照我的预期安排所有节点的顺序?

It just made the first level nodes in expected order. All sub-nodes are kept in original order. How can i make the order of all nodes as my expected ?

推荐答案

xsl:copy-of 也复制所有子节点,并且不计算它的子节点.

xsl:copy-of copies all child nodes as well and child nodes of it are not evaluated.

因此您的 TShip 和 TItems 模板从未被评估过. 复制所有 ....

So your TShip and TItems templates are never even being evaluated. <xsl:copy-of select="ship"> copies all of <ship>...</ship>.

对您的模板的这种修改将证明您的 TShip 和 TItems 模板没有被调用.

This modification to your template will demonstrate that your TShip and TItems templates are not being called.

<?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>

请注意,输出包含我添加的 元素.

Notice that the output does not contain the <test> elements I added.

您需要做的是是递归隐式复制.通常 xsl:copyxsl:copy-ofxsl:for-each 是不好的 xsl 模板设计的标志——有很多xsl:templatexsl:apply-template 带有身份转换的几个问题不能更好地处理.

What you need to do instead is recursive implicit copying. Usually xsl:copy, xsl:copy-of and xsl:for-each are a sign of bad xsl template design--there are very few problems which xsl:template and xsl:apply-template with an identity transform do not handle better.

我会这样做:

<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 对孩子们.

Notice how many fewer assumptions this template design makes about the structure of your source XML. It is also much easier to change: for example, if you want to silence or rename a particular element that may itself have children, you just add a new xsl:template that matches that element, do whatever you need to do, and xsl:apply-templates on the children.

您应该了解有关此 XSLT 模式的更多信息,因为它用途广泛并使模板创作变得不那么乏味,您的模板也不会那么脆弱.

You should learn more about this XSLT pattern because it is very versatile and makes template authoring much less tedious and your templates much less brittle.

这篇关于通过 xslt 重新排列 xml 节点,包括子节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆