【问题标题】:XSLT : Remove child nodes without repeating XPathXSLT:删除子节点而不重复 XPath
【发布时间】:2017-06-15 23:13:44
【问题描述】:

我有以下 XML:

<?xml version="1.0"?>
<Transaction>
    <Product>
        <ProductRq>
            <ContactInfo>
                <!-- several child elements -->
            </ContactInfo>
            <OrderInfo>
                <!-- several child elements -->
            </OrderInfo>
            <ProductInfo>
                <!-- several child elements -->
            </ProductInfo>
            <AddressInfo>
                <!-- several child elements -->
            </AddressInfo>
            <AuditInfo>
                <!-- several child elements -->
            </AuditInfo>
            <DeliveryInfo>
                <!-- several child elements -->
            </DeliveryInfo>
        </ProductRq>
    </Product>
</Transaction>

为了简洁起见,我在&lt;ProductRq&gt; 标记中省略了几个子元素,其中包含4 或5 个类似于&lt;OrderInfo&gt; 和&lt;CustomerInfo&gt; 的子元素,以及*Info 的子节点元素。

我需要删除 Order 和 &lt;CustomerInfo&gt; 等元素的子元素,同时保留 *Info 标记。在 7 个标签中,大约有 4 个要通过此过程。如果不重复,我想不出该怎么做:

<xsl:template match="Transcation/Product/ProductRq/<tag name here>/*" />

对于&lt;ProductRq&gt; 的每个孩子。有没有办法利用上下文节点(&lt;ProductRq&gt;) 并通过子节点循环,除上述之外删除他们自己的子节点?

编辑:
我添加了&lt;ProductRq&gt; 的剩余子标签。除&lt;AuditInfo&gt; 和&lt;ContactInfo&gt; 之外的所有标签都必须删除其子节点。

【问题讨论】:

  • 您实际上不需要在模板匹配中指定完整的 xpath,您只需执行&lt;xsl:template match="&lt;tag name here&gt;/*" /&gt;。如果同一元素可能出现在层次结构中的不同级别并且您需要区分它们,则只需使用完整路径对其进行限定。
  • 好吧,我认为 ProductRq 的一些子元素可能会在文档的后面针对不同的标签重复,但 ProducerRq 是唯一的,所以 ProducerRq/ 就足够了吗?
  • @jbailie1991 - 你真的需要展示那些遗漏的孩子,因为它们与你的问题非常相关。至少显示 ProductRq 的子级。

标签: xml xslt xpath


【解决方案1】:

一种解决方案是应用以下 XSLT:

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

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

  <!-- skip all child nodes of ProductRq except they are AuditInfo or ContactInfo -->
  <xsl:template match="ProductRq/*[not(self::AuditInfo | self::ContactInfo)]">       
    <xsl:element name="{name(.)}" />   <!-- so create an element with this name -->
  </xsl:template>

</xsl:stylesheet>

输出:

<?xml version="1.0"?>
<Transaction>
    <Product>
        <ProductRq>
            <ContactInfo>
                <!-- several child elements -->
            </ContactInfo>
            <OrderInfo/>
            <ProductInfo/>
            <AddressInfo/>
            <AuditInfo>
                <!-- several child elements -->
            </AuditInfo>
            <DeliveryInfo/>
        </ProductRq>
    </Product>
</Transaction>

【讨论】:

  • 所以第三个模板,如果我理解正确,匹配 ProductRq 的不是 AuditInfo 或 ContactInfo 的子节点,然后检查元素名称是否为空,如果不是,则创建一个新元素姓名?所以或多或少地用相同的标签覆盖匹配的元素,除了它是空的?
  • @jbailie1991:你是对的。阅读您的评论后,我还从答案中删除了多余的第二个模板。所以最后一个(前 3 个)模板只是重新创建所有不是 AuditInfo 或 ContactInfo 的元素(并且不属于其他节点类型 (name()=empty))而没有任何子节点。
  • 啊,这很酷。所以那些子节点总是存在的,即使是空的,所以这可以更进一步并放弃条件吗?只需用空白版本替换每个找到的元素?
  • @jbailie1991:我通过将ProductRq/node()[...] 替换为ProductRq/*[...],进一步改进了我的答案。这样只有元素(而不是所有节点)被选中,因此&lt;xsl:if... 检查空节点变得多余。
【解决方案2】:

我需要删除 Order 和 &lt;CustomerInfo&gt; 同时保留 *Info 标签。

你要找的模板是

<!-- XSLT 2.0 and up -->
<xsl:template match="ProductRq/*[ends-with(name(), 'Info')]/*" />


<!-- XSLT 1.0 variant, as ends-with() does not exist here -->
<xsl:template match="ProductRq/*[
  substring-before(name(), 'Info') != '' and substring-after(name(), 'Info') = ''
]/*" />

【讨论】:

  • 我喜欢 xslt 2 one liner,但这会删除所有以 Info 结尾的元素的所有子元素。对于我的用例,我需要保留 AuditInfo 和 ContactInfo 的子元素,并剥离其他 *Info 标记的子节点
  • @jbailie1991 这很简单,只需像and not(name() = 'KeepThis' or name() = 'KeepThat') 这样的表达式添加例外即可。
  • @jbailie1991 对我的回答有任何反馈吗?你有没有进一步尝试?它奏效了吗?如果没有,是什么没用?
  • 我选择了上面的答案。它允许我在不删除实际子元素本身的情况下剥离 ProductRq 子元素的子元素,同时跳过问题中概述的异常,而这会删除所有子元素,即使使用您发布的添加异常的方法,它仍然会删除标签完全而不是删除孩子的孩子,但保留空标签
【解决方案3】:

写一个匹配ProductRq的模板:

<xsl:template match="ProductRq">
  <xsl:copy>
    <xsl:apply-templates select="ContactInfo | OrderInfo | ProductInfo | AddressInfo"/>
  </xsl:copy>
</xsl:template>

如您所见,select 子句包含所有要包含的子节点。 默认情况下,它执行深层复制(带有子节点)。

但是您写道,某些节点(例如 ContactInfo 和 OrderInfo)只能在浅模式下复制。 (我假设这种模式应该只涉及复制子文本节点 但仅此而已。复制“空”标签似乎是一个奇怪的概念。)

为了只有这些节点的浅拷贝,再写一个模板:

<xsl:template match="ContactInfo | OrderInfo">
  <xsl:copy><xsl:apply-templates select="text()"/></xsl:copy>
</xsl:template>

总而言之,您将涉及的节点名称(用“|”分隔)写在两个地方:

  • 所有名字都是第一个模板,
  • 要在第二个模板中浅层复制的节点名称。

无论如何,你必须在某个地方写下这些名字。

【讨论】:

  • 我明白了,所以我会在第一个模板中指定所有 ProductRqs 子节点,并定义我希望在第二个模板中删除哪些子节点?此外,空标签用于下游系统。我不是 100% 知道他们为什么要在 xml 中添加一个 ,但我猜它是为了进行额外的处理(可能是验证;我的服务不需要,因为它位于 2 个队列之间,它的唯一工作是根据附加到 Exchange 对象的标头为其他系统转换 xml
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 1970-01-01
相关资源
最近更新 更多