使用 XSLT 将 XML 节点组合成单个节点

Posted

技术标签:

【中文标题】使用 XSLT 将 XML 节点组合成单个节点【英文标题】:Combining XML Nodes into a single node with an XSLT 【发布时间】:2019-04-11 08:41:21 【问题描述】:

我正在尝试使用转换编辑一些 XML,但我正在努力实现我想要的结果。

我有一些 XML:

<FX>
   <Order ATTRIBUTE1="ACTIVE" ATTRIBUTE2="CCY" />
   <Attribute NAME="N1" VALUE="V1" />
   <Attribute NAME="N2" VALUE="V2" />
   <Attribute NAME="N3" VALUE="V3" />
</FX>

我想把它改成这样:

<FX>
   <Order ATTRIBUTE1="ACTIVE" ATTRIBUTE2="CCY" />
   <Attribute NAME="N1, N2, N3" VALUE="V1,V2,V3" />
</FX>

这可能吗?任何人都可以提供有关如何使用转换执行此操作的任何建议吗?

【问题讨论】:

您的 XML 无效 你为什么这么说?我只是以此为例来说明我试图实现的基本目标...... 因为它无效(在您编辑之前)。我们无法猜测 XML 的正确结构是什么 顺便说一句,您真的需要使用 XSLT 吗? Linq to XML 是一种选择吗?还是 XML 序列化/反序列化? 我正在尝试这样做以避免需要更改代码。我有适当的架构来对传入的 XML 使用转换。如果我可以通过变换实现我想要的结果,那将是一场胜利。 【参考方案1】:

您可以使用以下与 Asp.NET 兼容的 XSLT-1.0 样式表来执行从源 XML 到目标 XML 的 XSLT 转换:

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

  <xsl:template match="/FX">
    <xsl:copy>
        <xsl:copy-of select="Order" />
        <Attribute>
            <xsl:attribute name="NAME">
                <xsl:for-each select="Attribute">
                    <xsl:value-of select="@NAME" />
                    <xsl:if test="position() != last()">
                        <xsl:text>, </xsl:text>
                    </xsl:if>
                </xsl:for-each>
            </xsl:attribute>
            <xsl:attribute name="VALUE">
                <xsl:for-each select="Attribute">
                    <xsl:value-of select="@VALUE" />
                    <xsl:if test="position() != last()">
                        <xsl:text>,</xsl:text>
                    </xsl:if>
                </xsl:for-each>
            </xsl:attribute>
        </Attribute>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

它的输出是:

<FX>
  <Order ATTRIBUTE1="ACTIVE" ATTRIBUTE2="CCY"/>
  <Attribute NAME="N1, N2, N3" VALUE="V1,V2,V3"/>
</FX>

【讨论】:

谢谢!这与我需要的非常接近。目标与 XML 的外观无关,我希望它保持不变,除了删除所有重复的 ATTRIBUTE 节点并将其替换为单个 ATTRIBUTE 节点,该节点包含原始消息中的所有名称/值对,以逗号表示NAME/VALUE 的分隔属性。 感谢您的回复,这是一个很好的开始!欣赏它。 “关闭”是什么意思。结果正是您在问题中所要求的。 我几乎拆开了我实际使用的 XML,试图说明我想要发生的事情。我的实际 XML 有一堆节点(有时会出现在 XML 中,有时不会出现)。我基本上试图保持 XML 的原样,唯一的变化是操作 Attribute 节点并将它们压缩为单个节点。对不起,我应该在我的帖子中更清楚。无论如何,我非常感谢您的宝贵时间和您的回复。 我用 替换了这一行:。现在,我得到了我的 XML 的精确副本,并添加了我想要的具有 csv 名称/值对的新属性节点。我现在只需要删除原来的。【参考方案2】:

一般来说,如果您想转换一些节点但保留其余节点,您可以使用身份转换模板作为起点,然后添加模板来更改您想要更改的那些节点:

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

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

  <xsl:template match="FX/Attribute[1]">
      <xsl:copy>
          <xsl:apply-templates select="@*"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="FX/Attribute[position() > 1]"/>

  <xsl:template match="FX/Attribute[1]/@*">
      <xsl:attribute name="name()">
          <xsl:for-each select=". | ../following-sibling::Attribute/@*[name() = name(current())]">
              <xsl:if test="position() > 1">,</xsl:if>
              <xsl:value-of select="."/>
          </xsl:for-each>
      </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/jyH9rNk

【讨论】:

非常感谢您的帮助!非常感谢您的宝贵时间!

以上是关于使用 XSLT 将 XML 节点组合成单个节点的主要内容,如果未能解决你的问题,请参考以下文章

XSLT 将来自多个节点的属性连接成单个值

XSLT将同名兄弟节点的值合并/连接到单个节点中

我们如何识别一组节点并将该组添加到另一个 XSLT 中 XML 源结构不同的节点?

如何使用 webpack 将多个节点 js 文件组合成一个包

使用 xslt 将 xml 复杂节点元素拆分为多个节点

使用 XSLT 添加强制节点