使用XSLT将XML排序为XML [英] Sort XML to XML using XSLT

查看:78
本文介绍了使用XSLT将XML排序为XML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了一些类似的问题,但是却努力将解决方案弯曲"到我需要的地方,因此很抱歉再次提出要求.

我有一些这样的XML....

<?xml version="1.0" encoding="UTF-8"?>

<ns:Root
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ns="urn:Test.Namespace"  
    xsi:schemaLocation="urn:Test.Namespace Test1.xsd"
    >
    <ns:element1 id="001">
        <ns:element2 id="001.1" order="1">
            <ns:element3 id="001.1.1" />
        </ns:element2>
        <ns:element2 id="001.2" order="2">
            <ns:element3 id="001.1.2" />
        </ns:element2>        
    </ns:element1>
    <ns:element1 id="003">
        <ns:element2 id="007.0" order="1">
            <ns:element3 id="007.1.1" />
        </ns:element2>
    </ns:element1>
    <ns:element1 id="002">
        <ns:element2 id="002.1" order="3">
            <ns:element3 id="002.1.1" />
        </ns:element2>
        <ns:element2 id="002.2" order="4">
            <ns:element3 id="002.1.2" />
        </ns:element2> 
    </ns:element1>    
</ns:Root>

我已经写了这个XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ns="urn:Test.Namespace"
                >
    <xsl:output indent="no" />
    <xsl:template match="text()[not(string-length(normalize-space()))]"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates>
            <xsl:sort select="/ns:Root/ns:element1/@id" />
            <xsl:copy-of select="." />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="ns:element1">
        <xsl:copy-of select="." />
        <xsl:apply-templates />        
    </xsl:template>

    <xsl:template match="ns:element2">
        <xsl:copy-of select="." />
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="ns:element3">
        <xsl:copy-of select="." />
    </xsl:template>

</xsl:stylesheet>

(我从这里如何对xml进行排序? >

我想要做的是使用此XSLT通过element1的id属性对我的原始XML进行排序,并生成XML.这样的想法是,一旦对它进行排序,我就可以使用其他XSLT进行处理以获得最终结果.

不幸的是,这并没有给我任何输出,这使我觉得这里确实有一个愚蠢的错字.某个地方,但我看不到.

任何帮助将不胜感激.

谢谢

解决方案

您所有的问题都在这里的匹配模板中

<xsl:template match="/">
    <xsl:apply-templates>
        <xsl:sort select="/ns:Root/ns:element1/@id" />
        <xsl:copy-of select="." />
    </xsl:apply-templates>
</xsl:template>

首先,\符号与文档级别元素匹配,该元素与根 ns:Root 不同,但与之相关.这就是说,当您执行<xsl:apply-templates>时,所有要选择的就是 ns:root 元素,其中只有一个,因此没有必要对其进行排序!

您可能首先需要像这样在根元素上进行匹配,将其复制,然后开始对子元素进行排序.

<xsl:template match="/*">
    <xsl:copy>
        <!-- Code to select and sort childrens -->
    </xsl:copy>
</xsl:template>

下一个问题是排序语句.您正在使用xpath表达式/ns:Root/ns:element1/@id,但这是绝对路径,而不是相对路径,因此只能使用文档中第一个 ns:element1 的@id属性./p>

假设您已经位于根元素上,并且假设它只有 ns:element1 个元素作为子元素,则可以做到这一点

    <xsl:apply-templates>
        <xsl:sort select="@id" />
    </xsl:apply-templates>

最后一个问题是您的 xsl:apply-templates 中有一个<xsl:copy-of select="." />语句,这是不允许的.如上所示,您可能应该在这里使用 xsl:copy .

值得指出的是,如果您还不了解的话,最好使用XSLT identity transform 即可复制现有元素,除非您想以某种方式进行更改.这样,您不必为每种特定类型的元素创建模板.

尝试以下XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ns="urn:TestNamespace"                >
    <xsl:output indent="yes" />

    <xsl:strip-space elements="*"/>

    <xsl:template match="text()[not(string-length(normalize-space()))]"/>

    <xsl:template match="/*">
        <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates>
            <xsl:sort select="@id" />
        </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

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

应用于XML时,将输出以下内容

<ns:Root xmlns:ns="urn:Test.Namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="urn:Test.Namespace Test1.xsd">
   <ns:element1 id="001">
      <ns:element2 id="001.1" order="1">
         <ns:element3 id="001.1.1"/>
      </ns:element2>
      <ns:element2 id="001.2" order="2">
         <ns:element3 id="001.1.2"/>
      </ns:element2>
   </ns:element1>
   <ns:element1 id="002">
      <ns:element2 id="002.1" order="3">
         <ns:element3 id="002.1.1"/>
      </ns:element2>
      <ns:element2 id="002.2" order="4">
         <ns:element3 id="002.1.2"/>
      </ns:element2>
   </ns:element1>
   <ns:element1 id="003">
      <ns:element2 id="007.0" order="1">
         <ns:element3 id="007.1.1"/>
      </ns:element2>
   </ns:element1>
</ns:Root>

I have a found a few similar questions, to this but struggled to 'bend' the solution to what I need, so apologies for asking again.

I have some XML like this....

<?xml version="1.0" encoding="UTF-8"?>

<ns:Root
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ns="urn:Test.Namespace"  
    xsi:schemaLocation="urn:Test.Namespace Test1.xsd"
    >
    <ns:element1 id="001">
        <ns:element2 id="001.1" order="1">
            <ns:element3 id="001.1.1" />
        </ns:element2>
        <ns:element2 id="001.2" order="2">
            <ns:element3 id="001.1.2" />
        </ns:element2>        
    </ns:element1>
    <ns:element1 id="003">
        <ns:element2 id="007.0" order="1">
            <ns:element3 id="007.1.1" />
        </ns:element2>
    </ns:element1>
    <ns:element1 id="002">
        <ns:element2 id="002.1" order="3">
            <ns:element3 id="002.1.1" />
        </ns:element2>
        <ns:element2 id="002.2" order="4">
            <ns:element3 id="002.1.2" />
        </ns:element2> 
    </ns:element1>    
</ns:Root>

I have written this XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ns="urn:Test.Namespace"
                >
    <xsl:output indent="no" />
    <xsl:template match="text()[not(string-length(normalize-space()))]"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:apply-templates>
            <xsl:sort select="/ns:Root/ns:element1/@id" />
            <xsl:copy-of select="." />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="ns:element1">
        <xsl:copy-of select="." />
        <xsl:apply-templates />        
    </xsl:template>

    <xsl:template match="ns:element2">
        <xsl:copy-of select="." />
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="ns:element3">
        <xsl:copy-of select="." />
    </xsl:template>

</xsl:stylesheet>

(I cribbed the outline for this from here how to sort xml?)

What I want to be able to do is use this XSLT to sort my original XML by the id attribute of element1 and produce XML. The idea being that once it is sorted, then I can process with some other XSLT to get the final result.

Unfortunately this does not give me any output, which makes me think there is a really stupid typo. somewhere, but I cannot see it.

Any help would be appreciated.

Thanks

解决方案

Your problems all lie in this matching template here

<xsl:template match="/">
    <xsl:apply-templates>
        <xsl:sort select="/ns:Root/ns:element1/@id" />
        <xsl:copy-of select="." />
    </xsl:apply-templates>
</xsl:template>

Firstly the \ symbol matches the document level element, which is not the same as the root ns:Root, but one level about it. What this means is that when you do <xsl:apply-templates> all that will select is the ns:root element, of which there is only one, and so no point sorting it!

What you probably need to start of with is to match on the root element like so, copy it and then start sorting the children.

<xsl:template match="/*">
    <xsl:copy>
        <!-- Code to select and sort childrens -->
    </xsl:copy>
</xsl:template>

The next problem you have is with the sort statement. You are using the xpath expression /ns:Root/ns:element1/@id, but this is an absolute path, not a relative one, so will only ever pick up the @id attribute of the first ns:element1 in the document.

Assuming you were positioned on the root element already, and assuming it only had ns:element1 elements as children, you could just this

    <xsl:apply-templates>
        <xsl:sort select="@id" />
    </xsl:apply-templates>

The final problem you have is that you have an <xsl:copy-of select="." /> statement in your xsl:apply-templates which is not allowed. You probably should be using xsl:copy here, as shown above.

It is also worth pointing out, if you weren't aware already, that it is better to use the XSLT identity transform to copy existing elements, unless you want to change them in some way. That way you don't have to create templates for each particular type of element.

Try the following XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:ns="urn:TestNamespace"                >
    <xsl:output indent="yes" />

    <xsl:strip-space elements="*"/>

    <xsl:template match="text()[not(string-length(normalize-space()))]"/>

    <xsl:template match="/*">
        <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates>
            <xsl:sort select="@id" />
        </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

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

When applied to your XML, the following is output

<ns:Root xmlns:ns="urn:Test.Namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="urn:Test.Namespace Test1.xsd">
   <ns:element1 id="001">
      <ns:element2 id="001.1" order="1">
         <ns:element3 id="001.1.1"/>
      </ns:element2>
      <ns:element2 id="001.2" order="2">
         <ns:element3 id="001.1.2"/>
      </ns:element2>
   </ns:element1>
   <ns:element1 id="002">
      <ns:element2 id="002.1" order="3">
         <ns:element3 id="002.1.1"/>
      </ns:element2>
      <ns:element2 id="002.2" order="4">
         <ns:element3 id="002.1.2"/>
      </ns:element2>
   </ns:element1>
   <ns:element1 id="003">
      <ns:element2 id="007.0" order="1">
         <ns:element3 id="007.1.1"/>
      </ns:element2>
   </ns:element1>
</ns:Root>

这篇关于使用XSLT将XML排序为XML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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