【问题标题】:Generic Xml Document manipulation通用 XML 文档操作
【发布时间】:2009-07-23 08:59:41
【问题描述】:

我们的 xml 文档包含许多标记节点,例如 isProduct、isActive、isMandatory,其中节点文本可能是 True 或 False。

需要对文档进行操作并保持其结构,但将上述节点转换为如下的语言表示:

< isProduct >True</ isProduct >   ===>   <Type>Product<Type>
< isProduct >False</ isProduct >  ===>   <Type/>

其他标志节点也是如此。

我们正在寻求一种可扩展且可扩展的解决方案,该解决方案可以在部署后以最小的摩擦进行配置。

通过可扩展;我们的意思是会有更多的案例;像 2 个代表状态的标志;即 isEmployee 和 isCustomer 在文档中用于表示 4 个不同的命名事物。因此,这 4 种可能的组合只能转换为一个字符串,例如“Employee”、“Customer”、“Customer-Employee”或“None”。

通过可扩展;我们的意思是,它可以用于处理任何 XML 文档,而无需事先了解其架构并且对文档大小没有限制。

我们知道这可以使用 XSLT 来完成,我们是否可以编写一个 XSLT 来接受任何文档并生成添加或更新其他节点的相同文档?

【问题讨论】:

  • 那么如果一个节点同时有&lt;isProduct&gt;True&lt;/isProduct&gt;&lt;isEmployee&gt;True&lt;/isEmployee&gt;应该怎么办?

标签: xml xslt translation transformation


【解决方案1】:

假设这样的输入:

<gizmo>
  <isProduct>True</isProduct>
  <isFoo>False</isFoo>
  <isBar>True</isBar>
</gizmo>

通用方法是:

<xsl:template match="gizmo">
  <xsl:copy>
    <xsl:apply-templates select="*" />
  </xsl:copy>
</xsl:template>

<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
  <Type>
    <xsl:if test=". = 'True'">
      <xsl:value-of select="substring-after(local-name(), 'is')" />
    </xsl:if>
  </Type>
</xsl:template>

产生:

<gizmo>
  <Type>Product</Type>
  <Type />
  <Type>Bar</Type>
</gizmo>

更通用的方法使用(大量)修改的身份转换:

<!-- the identity template... well, sort of -->
<xsl:template match="node() | @*">
  <xsl:copy>
    <!-- all element-type children that begin with 'is' -->
    <xsl:variable name="typeNodes"  select="
      *[substring(local-name(), 1, 2) = 'is']
    " />

    <!-- all other children (incl. elements that don't begin with 'this ' -->
    <xsl:variable name="otherNodes" select="
      @* | node()[not(self::*) or self::*[substring(local-name(), 1, 2) != 'is']]
    " />

    <!-- identity transform all the "other" nodes -->
    <xsl:apply-templates select="$otherNodes" />

    <!-- collapse all the "type" nodes into a string -->
    <xsl:if test="$typeNodes">
      <Type>
        <xsl:variable name="typeString">
          <xsl:apply-templates select="$typeNodes" />
        </xsl:variable>
        <xsl:value-of select="substring-after($typeString, '-')" />
      </Type>
    </xsl:if>
  </xsl:copy>
</xsl:template>

<!-- this collapses all the "type" nodes into a string -->
<xsl:template match="*[substring(local-name(), 1, 2) = 'is']">
  <xsl:if test=". = 'True'">
    <xsl:text>-</xsl:text>
    <xsl:value-of select="substring-after(local-name(), 'is')" />
  </xsl:if>
</xsl:template>

<!-- prevent the output of empty text nodes -->
<xsl:template match="text()">
  <xsl:if test="normalize-space() != ''">
    <xsl:value-of select="." />
  </xsl:if>
</xsl:template>

以上内容采用任何 XML 输入并输出相同的结构,只有名为 &lt;is*&gt; 的元素以破折号分隔的字符串形式折叠到单个 &lt;Type&gt; 节点中:

<!-- in -->
<foo>
  <fancyNode />
  <gizmo>
    <isProduct>True</isProduct>
    <isFoo>False</isFoo>
    <isBar>True</isBar>
  </gizmo>
</foo>

<!-- out -->
<foo>
  <fancyNode />
  <gizmo>
    <Type>Product-Bar</Type>
  </gizmo>
</foo>

【讨论】:

  • 复制模板也会从 XML 文档中去除所有属性。
  • 是的,我知道。在我的第一个示例中,我想说明如何选择和处理“有趣”的节点。我添加了一个通用解决方案作为第二种方法来做更多 OP 描述的事情。
【解决方案2】:

这是一个基于身份转换的XSLT解决方案:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="isProduct">
    <xsl:choose>
      <xsl:when test=". = 'True'"><Type>Product</Type></xsl:when>
      <xsl:otherwise><Type/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

【讨论】:

  • 那么这将如何处理 isEmployee 节点?
  • 要么添加一个与“isEmployee”匹配的类似模板,要么像 Tomalak 的回答那样使给定的模板通用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 2012-06-24
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
相关资源
最近更新 更多