通用Xml文档操作

时间:2009-07-23 08:59:41

标签: xml xslt translation transformation

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

需要操纵文档并保留其结构,但将上述节点转换为如下所示的语言表示:

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

其他标志节点也一样。

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

可扩展;我们的意思是会有更多的案件;比如表示状态的2个标志;即isEmployee和isCustomer在文档中用于表示4种不同的命名事物。因此,4种可能的组合应该只转换成一个字符串,如“员工”,“客户”,“客户 - 员工”或“无”。

可扩展;我们的意思是它可用于处理任何XML文档,而无需事先了解其架构,也不会限制文档大小。

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

2 个答案:

答案 0 :(得分:2)

假设输入如下:

<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输入并输出相同的结构,只有名为<is*>的元素作为划线分隔的字符串折叠到单个<Type>节点中:

<!-- 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>

答案 1 :(得分:1)

以下是基于身份转换的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>