如何将XML元素数组转换为序列?

时间:2014-06-07 05:25:16

标签: xml xslt

我遇到了一个问题,这个问题是从XML到JSON的再次调解,然后问题是我需要使用的库在使用JSON数组方面做得不好。基本上调解的结果是:

<Continents>
    <ContinentName>
        <element>North America</element>
        <element>Asia</element>
    </ContinentName>
</Continents>

但我想要的是一系列大陆名称:

<Continents>
    <ContinentName>North America<ContinentName>
    <ContinentName>Asia<ContinentName>
<Continents>

有没有办法在XSL中轻松完成这项工作?

谢谢,

伊恩

添加几个更好的示例来显示整个文档。可悲的是,关于这是通用的还是具体的问题的答案是它的具体情况。标签由我们使用的JSON库插入:

示例1,结构简单,虽然在这种情况下,只需要将Animals / element节点重命名为,但这很容易。这是我感到困惑的大陆作品。

<?xml version="1.0" encoding="UTF-8"?>
<Animals>
    <element>
        <SpeciesName>Grizzly Bear</SpeciesName>
        <Continents>
            <ContinentName>
                <element>North America</element>
                <element>Asia</element>
                <element>Europe</element>
            </ContinentName>
        </Continents>
        <Population>867</Population>
        <href>http://fazio.loc/rest/animal/5a559e67-475b-41e8-9fdc-00359be1d4e2</href>
        <Id>5a559e67-475b-41e8-9fdc-00359be1d4e2</Id>
    </element>
    <element>
        <SpeciesName>Black Bear</SpeciesName>
        <IdentificationDate>1897-10-20</IdentificationDate>
        <Continents>
            <ContinentName>
                <element>North America</element>
                <element>Europe</element>
            </ContinentName>
        </Continents>
        <Population>11054</Population>
        <href>http://fazio.loc/rest/animal/f2e020e4-93ab-4d9b-b7b2-63082e2eaf06</href>
        <Id>f2e020e4-93ab-4d9b-b7b2-63082e2eaf06</Id>
    </element>
</Animals>

示例2更复杂,因为LineItem数组可以包含一个或多个具有不同结果的元素:

<?xml version="1.0" encoding="UTF-8"?>
  <Orders>
    <element>
        <Description>First Order</Description>
        <Status>New</Status>
        <TotalCost>$45</TotalCost>
        <LineItems>
            <LineItem>
                <Name>Socks</Name>
                <Price>$15</Price>
                <Quantity>3</Quantity>
                <Total>$45</Total>
            </LineItem>
        </LineItems>
        <href>http://fazio.loc/rest/orderStatus/0d06dc7d-2491-4fa9-9e49-921b4cb9934a</href>
        <Id>0d06dc7d-2491-4fa9-9e49-921b4cb9934a</Id>
    </element>
    <element>
        <Description>First Order</Description>
        <Status>New</Status>
        <TotalCost>$80</TotalCost>
        <LineItems>
            <LineItem>
                <element>
                    <Name>Socks</Name>
                    <Price>$15</Price>
                    <Quantity>3</Quantity>
                    <Total>$45</Total>
                </element>
                <element>
                    <Name>Pants</Name>
                    <Price>$35</Price>
                    <Quantity>1</Quantity>
                    <Total>$35</Total>
                </element>
            </LineItem>
        </LineItems>
        <href>http://fazio.loc/rest/orderStatus/c929e8bc-054a-4ffc-86b9-b42af63d5537</href>
        <Id>c929e8bc-054a-4ffc-86b9-b42af63d5537</Id>
    </element>
  </Orders>

我真的很讨厌这样做,但我对这些消息有一个变体,建议的解决方案并不起作用。这就是内心并不深刻的地方。即文档看起来像:

    <?xml version="1.0" encoding="UTF-8"?>
    <Order 
        xmlns="http://demo.soa.com/order/1.0">
        <Description >First Order</Description>
        <Status >New</Status>
        <TotalCost >$80</TotalCost>
        <LineItems >
            <LineItem>
                <element>
                    <Name>Socks</Name>
                    <Price>$15</Price>
                    <Quantity>3</Quantity>
                    <Total>$45</Total>
                </element>
                <element>
                    <Name>Pants</Name>
                    <Price>$35</Price>
                    <Quantity>1</Quantity>
                    <Total>$35</Total>
                </element>
            </LineItem>
        </LineItems>
        <href >http://fazio.loc/rest/orderStatus/c929e8bc-054a-4ffc-86b9-b42af63d5537</href>
        <Id >c929e8bc-054a-4ffc-86b9-b42af63d5537</Id>
    </Order>

我无法理解与众不同之处,但在这种情况下,似乎并不匹配,因此不会被替换。

1 个答案:

答案 0 :(得分:2)

这是一个可能的解决方案:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="*[child::element][parent::*]">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="element">
        <xsl:element name="{name(parent::*)}">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

此XSLT样式表包含三个模板。第一个称为身份转换,因为它只是将任何节点(属性,命名空间,注释,处理指令和元素)从源树复制到结果树。

第二个模板跳过复制元素如果它有一个element,那么如果它不是根节点(它有一个父母)。内部数组遵循模式Continents/ContinentName/elementLineItems/LineItem/element,但与根元素不同:Orders/element(而不是Orders/Order/element或类似的东西) 。为了弥补这一点,元素是重复的,因此生成的XML格式正确。

第三个模板与element匹配,并在其位置复制parent元素的名称(在第二个模板中跳过。

以下是一些可以在线试验的工作小提琴:

  1. Fiddle no. 1
  2. Fiddle no. 2
  3. 编辑1 - 更改元素的名称:由于源中没有OrderAnimal,因此根目录是Orders/Order而不是Orders/Orders,我们可以制作从父级复制的元素名称并切断s(当然,它不会像Wolves/Wolf这样的集合很好地工作) 。只需将此模板添加到样式表:

    <xsl:template match="element[parent::*[not(parent::*)]]">
        <xsl:element name="{substring(name(parent::*),1,string-length(name(parent::*))-1)}">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    

    此模板选择其父级没有父级(父级是 root 节点)的element个节点,并将其替换为父级的名称减去最后一个字符(如果父级是{ {1}},它会创建Animals;如果父项为Animal,则会创建Root

    <强> Fiddle no. 3

    编辑2 - 添加默认命名空间:要向整个文档添加默认命名空间,您只需要1)将Roo添加到xmlns="your-namespace"和2)以匹配root并创建<xsl:stylesheet>,在<xsl:element>属性中提供相同的命名空间。只需将此模板添加到样式表中即可:

    namespace

    <强> Fiddle no. 4

    编辑3 - 处理现有命名空间:您的上一个示例不起作用,因为它已经声明了命名空间。样式表中任何未加前缀的元素选择器都被视为属于 no namespace 。要从源中选择属于<xsl:template match="*"> <xsl:element namespace="your-namespace" name="{name(.)}"> <xsl:apply-templates/> </xsl:element> </xsl:template> 的{​​{1}}元素,您必须在样式表中再次声明该命名空间,这次使用前缀:

    element

    前缀所有显式选项。在样式表中,使用通配符选择所有元素, {http://demo.soa.com/order/1.0}:element除外。所以你只需要为<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns="http://demo.soa.com/order/1.0" <!-- for the output tree --> xmlns:demo="http://demo.soa.com/order/1.0"> <!-- for use in XPath input -->

    的出现加上前缀
    element

    element

    以及<xsl:template match="*[child::demo:element][parent::*]"> ... 出现在编辑中添加的额外模板中的任何其他位置。

    通常推荐的解决方案。在您的情况下,由于样式表必须处理不同的来源,可能位于不同的名称空间,而<xsl:template match="demo:element"> 元素不应该是它的一部分(它&#39;由其他软件引入的外来对象),那么最佳方法可能是忽略命名空间。您可以使用XPath通过选择使用通配符的节点,然后在谓词中与其本地名称进行比较来实现。

    您只需要将每个出现的element替换为:

    element

    例如:

    element

    (BTW *[local-name()='element'] <xsl:template match="*[child::*[local-name()='element']][parent::*]"> 意思相同:我只是为了清晰起见而想要使轴明确,但我可以将其删除)

    <强> Fiddle no. 5

    如果你总是在你的源中有一个命名空间,并且你只想将它复制到结果中,那么这个样式表将不会为你做到这一点。您仍然必须在样式表中声明它。然而, 是一个解决方案,它包括使用child::element轴在创建每个新元素时将源中声明的名称空间复制到结果中:

    element

    如果您的源中未声明默认命名空间,则此解决方案将失败。

    <强> Fiddle no. 6