XSLT:如何在复制时删除生成的结果树片段的元素?

时间:2017-06-27 22:35:38

标签: xslt

我的目标是提取SOAP主体的内容,例如。 ElementsToExtract节点 - 但节点名称基本上可以是任意的:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
    <MessageId>52DF2371-4094-4408-A3EA-42D73FD1B7A3</MessageId>
  </soap:Header>
  <soap:Body>
    <ElementsToExtract>
        ...
        <RemoveMe>...</RemoveMe>
        <RemoveMeAlso>...</RemoveMeAlso>
        ...
    </ElementsToExtract>
  </soap:Body>
</soap:Envelope>

在我提取内容时,我想摆脱所有源文档共有的两个元素 - 比如RemoveMeRemoveMeAlso。由于更深层次的嵌套节点可能被称为相同,因此只能从ElementsToExtract节点下面的层中剥离它们。我该如何表达这个表达?

以下是我现在所做的事情:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
                xmlns:exsl="http://exslt.org/common"
                exclude-result-prefixes="soap exsl">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
  <xsl:strip-space elements="*"/>

  <xsl:variable name="SoapHeaderContents" select="exsl:node-set(soap:Envelope/soap:Header/*)"/>
  <xsl:variable name="SoapBodyContents" select="exsl:node-set(soap:Envelope/soap:Body/*)"/>

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

  <xsl:template match="/">
    <xsl:apply-templates select="$SoapBodyContents"/>
  </xsl:template>

  <!-- This is global, how to restrict to the ElementsToExtract element? -->
  <xsl:template match="node()[name() = 'RemoveMe']"/>
  <xsl:template match="node()[name() = 'RemoveMeAlso']"/>
</xsl:stylesheet>

我也玩node-set()函数,读过一个人不能修改结果树片段(它们只是文本节点?),但我不太明白如何解决那个结果节点组。所以没有删除节点:

<xsl:template match="/">
  <xsl:apply-templates select="$SoapBodyContents"/>
  <xsl:apply-templates select="$SoapBodyContents/RemoveMe" mode="m1"/>
</xsl:template>
<xsl:template name="StripRemoveMe" match="RemoveMe" mode="m1"/>

我也阅读了规范的某些部分,但无济于事。我迷失了线索。有人可以指导我采用正确的方法吗?

2 个答案:

答案 0 :(得分:0)

这对你有用吗?

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
exclude-result-prefixes="soap">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<!-- skip soap wrappers -->
<xsl:template match="/soap:Envelope">
    <xsl:apply-templates select="soap:Body/ElementsToExtract"/>
</xsl:template>

<!-- remove unwanted elements -->
<xsl:template match="ElementsToExtract/RemoveMe | ElementsToExtract/RemoveMeAlso"/>

</xsl:stylesheet>

在(不太可能)的情况下,您不知道ElementsToExtract元素的名称,您可以使用:

<!-- skip soap wrappers -->
<xsl:template match="/soap:Envelope">
    <xsl:apply-templates select="soap:Body/*"/>
</xsl:template>

<!-- remove unwanted elements -->
<xsl:template match="soap:Body/*/RemoveMe | soap:Body/*/RemoveMeAlso"/>

答案 1 :(得分:0)

一些快速的想法。

  • 您可以创建用于存储SOAP标头和正文的变量。这些已经在输入文档中,因此只编写与这些匹配的模板更有意义。

  • 虽然您为SOAP标头创建了一个变量,但您从不在任何地方使用它。

  • 如果您尝试连续应用模板,就像在示例XSL代码中一样,您将从第一个apply-templates获取所有输出节点,然后从下一个{{1获取所有输出节点}}。如果这些节点以任何方式交错,则这种方法不会产生可行的输出。

这是您的示例输入XML的修订版本,添加了一些我们想要保留的元素。

apply-templates

这是我们想要的输出:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
    <MessageId>52DF2371-4094-4408-A3EA-42D73FD1B7A3</MessageId>
  </soap:Header>
  <soap:Body>
    <ElementsToExtract>
        <KeepMe>This text will persist in the output.</KeepMe>
        <RemoveMe>This is text that will be removed.</RemoveMe>
        <RemoveMeAlso>This will also vanish from the output.</RemoveMeAlso>
        <OtherElementToKeep>And this one will also be kept.</OtherElementToKeep>
    </ElementsToExtract>
  </soap:Body>
</soap:Envelope>

这个XSL 1.0代码将完成这项工作。我从你的帖子中猜到你不熟悉XSL处理流程,所以我添加了评论来帮助解释发生了什么。

<?xml version="1.0" encoding="utf-8"?>
<ElementsToExtract>
    <KeepMe>This text will persist in the output.</KeepMe>
    <OtherElementToKeep>And this one will also be kept.</OtherElementToKeep>
</ElementsToExtract>

请注意,输出中最外面的元素是<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:soap="http://www.w3.org/2003/05/soap-envelope" version="1.0" exclude-result-prefixes="soap"> <xsl:strip-space elements="*"/> <xsl:output method="xml" indent="yes"/> <!-- The `/` matches the _logical root_ of the input file. This is basically equivalent to the start of the file, NOT the first element. This is a common place to start processing in XSL. --> <xsl:template match="/"> <!-- We just apply templates. In your case, we know already that we DON'T want to process everything: we want to leave certain things out, including a lot of the outermost elements. So we specify what to target in the `select` statement. --> <xsl:apply-templates select="soap:Envelope/soap:Body/ElementsToExtract"/> </xsl:template> <!-- This is the "identity" template, so called because it just copies over applicable matches identically. A template with a more-specific match statement takes precedence. --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- Here, we specify exactly those elements that are in the processing flow, and that we want to exclude from the output. Since `soap:Header` etc. are NOT in the processing flow (their element trees were never included in a preceding call to `apply-templates`), we don't need to worry about those. --> <xsl:template match="RemoveMe | RemoveMeAlso"/> </xsl:stylesheet> 。此元素将包含ElementsToExtract命名空间声明,即使此命名空间未在任何输出元素中使用(至少对于此小样本输入XML)。

如果您可以使用XSL 2.0+并且想要从输出中删除此命名空间,则可以将xmlns:soap="http://www.w3.org/2003/05/soap-envelope"属性添加到copy-namespaces="no"元素。