首页 > 解决方案 > XSLT 转换合并值

问题描述

我有一个格式如下的 XML 输入文档:

<Label>
    <Person>
        <Hash>12345</Hash>
        <Id>123123</Id>
        <Firstname>John</Firstname>
        <Lastname>Doe</Lastname>
        <Category>Business</Category>
    </Person>
    <Person>
        <Hash>12345</Hash>
        <Id>456789<Id>
        <Fistname>John</Firstname>
        <Lastname>Doe</Lastname>
        <Category>Information</Category>
    </Person>
</Label>

我想合并 Person 中的所有子节点,这会产生以下输出文档:

<Label>
    <Person>
        <Hash>12345</Hash>
        <Id>123123, 456789</Id>
        <Firstname>John</Firstname>
        <Lastname>Doe</Lastname>
        <Category>Business, Information</Category>
    <Person>
</Label>

所以实际上将 Person 中的所有节点合并为一个。最好将相同的值合并为一个值,但这不是必须的。因此,以下转换也是可以接受的:

<Label>
    <Person>
        <Hash>12345, 12345</Hash>
        <Id>123123, 456789</Id>
        <Firstname>John, John</Firstname>
        <Lastname>Doe, Doe</Lastname>
        <Category>Business, Information</Category>
    <Person>
</Label>

任何关于如何实现这一点的建议都非常受欢迎!

最好在 xslt 1.0 中进行转换

标签: xmlxslttransformation

解决方案


为了使查找字段值稍微简单一些,您可以定义一个键。

  <xsl:key name="field" match="Person/*" use="local-name()" />

然后,您只需要选择第一个Person元素的子节点,并为每个子节点使用键来键值...

  <xsl:for-each select="key('field', local-name())">
    <xsl:if test="position() > 1">, </xsl:if>
    <xsl:value-of select="." />
  </xsl:for-each>

试试这个 XSLT,它将重复项留在

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

  <xsl:key name="field" match="Person/*" use="local-name()" />

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

  <xsl:template match="Person[1]/*">
    <xsl:copy>
      <xsl:for-each select="key('field', local-name())">
        <xsl:if test="position() > 1">, </xsl:if>
        <xsl:value-of select="." />
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Person[position() > 1]" />
</xsl:stylesheet>

如果您确实想删除重复项,则需要使用 Muenchian Grouping。这意味着声明第二个密钥

 <xsl:key name="fieldAndValue" match="Person/*" use="concat(local-name(), ':', .)" />

要获得不同的值,请更改xsl:for-each如下:

<xsl:for-each select="key('field', local-name())[generate-id() = generate-id(key('fieldAndValue', concat(local-name(), ':', .))[1])]">

也试试这个 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:key name="field" match="Person/*" use="local-name()" />
  <xsl:key name="fieldAndValue" match="Person/*" use="concat(local-name(), ':', .)" />

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

  <xsl:template match="Person[1]/*">
    <xsl:copy>
      <xsl:for-each select="key('field', local-name())[generate-id() = generate-id(key('fieldAndValue', concat(local-name(), ':', .))[1])]">
        <xsl:if test="position() > 1">, </xsl:if>
        <xsl:value-of select="." />
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Person[position() > 1]" />
</xsl:stylesheet>

当然,如果您可以使用 XSLT 2.0,您可以更简单...

<xsl:template match="Person[1]/*">
  <xsl:copy>
    <xsl:value-of select="distinct-values(key('field', local-name()))" separator=", " />
  </xsl:copy>
</xsl:template>

编辑:如果您不能使用xsl:key,则将xsl:for-each(在不删除重复项的第一个 XSLT 中)更改为此....

 <xsl:for-each select="//Person/*[local-name() = local-name(current())]">

这将保留重复项。可以在没有密钥的情况下删除重复项,但可能比它的价值太麻烦......


推荐阅读