【问题标题】:Rearrange XML siblings using XSLT使用 XSLT 重新排列 XML 同级
【发布时间】:2018-10-31 05:50:23
【问题描述】:

我正在使用 XSLT 将 XML 文档转换为具有更多意义的新 XML 文档。 源 XML:

<root>
  <A>
    <country>Italy</country>
    <city>Rome</city>
    <score>13</score>
  </A>
  <A>
    <country>Italy</country>
    <city>Florence</city>
    <score>14</score>
  </A>
  <A>
    <country>France</country>
    <city>Paris</city>
    <score>20</score>
  </A>
</root>

节点&lt;country&gt;&lt;city&gt;&lt;score&gt; 都是兄弟节点。 我的问题是:如何在 XSLT 中像这样重新排列兄弟姐妹?

<country>
  <city>
    <score>
    </score>
  </city>
</country>

我期望的 XML:

<root>
  <Italy>
    <Rome>
      <score>13</score>
    </Rome>
    <Florence>
      <score>14</score>
    </Florence>
  </Italy>
  <France>
    <Paris>
      <score>20</score>
    </Paris>
  </France>
</root>

【问题讨论】:

  • 你可以使用 XSLT-2.0 吗?
  • @zx485 是的。谢谢你的回答。

标签: xml xslt


【解决方案1】:

XSLT-1.0 解决方案如下。它使用Muenchian Grouping 作为获取唯一国家/地区值的方法。

编辑:
为了确保元素名称是有效的 QNames,我添加了一个 translate(...) 表达式,它将相应城市名称或国家名称中的所有空格转换为下划线。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="countries" match="A" use="country" />

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

    <xsl:template match="text()" />    

    <xsl:template match="A[generate-id(.) = generate-id(key('countries',country)[1])]">
        <xsl:element name="{translate(country,' ','_')}">
            <xsl:for-each select="key('countries',country)">
                <xsl:element name="{translate(city,' ','_')}">
                    <xsl:copy-of select="score" />
                </xsl:element>           
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

XSLT-2.0 的方案更简单,因为它可以使用xsl:for-each-group:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/*">
        <xsl:copy>
            <xsl:for-each-group select="A" group-by="country">
                <xsl:element name="{translate(current-grouping-key(),' ','_')}">
                    <xsl:for-each select="current-group()">
                        <xsl:element name="{translate(city,' ','_')}">
                            <xsl:copy-of select="score" />
                        </xsl:element>           
                    </xsl:for-each>
                </xsl:element>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

两种方法的输出是一样的:

<?xml version="1.0"?>
<root>
    <Italy>
        <Rome>
            <score>13</score>
        </Rome>
        <Florence>
            <score>14</score>
        </Florence>
    </Italy>
    <France>
        <Paris>
            <score>20</score>
        </Paris>
    </France>
</root>

【讨论】:

  • 您可以使用current-grouping-key(),而不是current-group()[1]/country
  • 还值得添加一条评论,说明如何尝试从“美利坚合众国”之类的国家/地区创建元素名称将导致无效的 QName 错误(对于不是有效的 QNames)。希望请求的输出只是一个简单的示例,并且 OP 真的没有尝试根据国家/城市创建元素名称。
  • @DanielHaley:感谢您的建议。我为 QName 问题添加了一个(部分)解决方案。
猜你喜欢
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 2013-04-24
  • 1970-01-01
相关资源
最近更新 更多