XSLT:确保存在具有默认内容的某些元素

时间:2011-06-15 14:23:59

标签: xslt xslt-1.0

我需要确保XML文档始终包含这些节点:

<group>
    <section>0001</section>
    <head>0002</head>
    <body>0003</body>
</group>

典型的XML输入文件如下所示(组节点应始终位于类别和摘要之前):

<story>
    <group>
        <section>section-content</section>
        <head>head-content</head>
        <body>body-content</body>
        <extra>extra-content</extra>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

但无法保证组元素或其任何子元素都存在。

所以给出了一个XML文档:

<story>
    <category>some text</category>
    <summary>some text</summary>
</story>

输出应如下所示:

<story>
    <group>
        <section>0001</section>
        <head>0002</head>
        <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

XSLT不应修改现有内容,例如

<story>
    <group>
        <section>existing text</section>
        <extra>existing text</extra>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

应转换为:

<story>
    <group>
        <section>existing text</section>
        <head>0002</head>
        <body>0003</body>
        <extra>existing text</extra>
    </group>
    <category>some text</category>
    <summary>some text</summary>
</story>

Tomalak's答案为依据,我提出了这个问题:

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

<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="utf-8" indent="yes"/>

  <!-- These defaults (elements and contents) can be modified at any time -->
  <subst:defaults>
    <subst:element name="group">
    <subst:section>0001</subst:section>
    <subst:head>0002</subst:head>
    <subst:body>0002</subst:body>
    </subst:element>
  </subst:defaults>

<!-- this makes the above available as a variable -->
<xsl:variable name="defaults" select="document('')/*/subst:defaults" />

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

<xsl:template match="story[not(group)]">
  <xsl:copy>
   <xsl:apply-templates select="@*"/>
      <xsl:element name="group"></xsl:element>
   <xsl:apply-templates select="node()"/>
  </xsl:copy>
 </xsl:template>

  <xsl:template match="group">
    <xsl:copy>
      <xsl:copy-of select="@*|*"/>
      <xsl:call-template name="create-defaults" />
    </xsl:copy>
  </xsl:template>

  <!-- Insert the defaults-->
  <xsl:template name="create-defaults">
    <xsl:variable name="this" select="." />
    <xsl:for-each select="$defaults/subst:element[@name = name($this)]/*">
      <xsl:if test="not($this/*[name() = local-name(current())])">
        <xsl:apply-templates select="." />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

<!-- Remove the namespaces -->
  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

如果group元素已经存在似乎有效,但如果group元素不存在,我无法弄清楚如何让它工作。

1 个答案:

答案 0 :(得分:1)

此XSLT 1.0转换

<xsl:stylesheet   version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:subst="http://tempuri.com/mysubst"
  exclude-result-prefixes="subst"
>
  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" encoding="utf-8" indent="yes"/>

  <subst:defaults>
    <subst:element name="group">
      <subst:element name="section">0001</subst:element>
      <subst:element name="head">0002</subst:element>
      <subst:element name="body">0003</subst:element>
    </subst:element>
  </subst:defaults>

  <xsl:variable name="subst" select="document('')/*/subst:defaults/subst:element" />

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

  <!-- subst:element outputs a new element with the given @name -->
  <xsl:template match="subst:element">
    <xsl:element name="{@name}">
      <!-- this would also copy any additional "default" attribute! -->
      <xsl:apply-templates select="@*[not(name() = 'name')] | node()"/>
    </xsl:element>
  </xsl:template>

  <!-- only stories without any group get "special" treatment -->
  <xsl:template match="story[not(group)]">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="$subst[@name = 'group']" mode="copy-or-default" />
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <!-- a group first outputs all "default" children, then any extra children -->    
  <xsl:template match="group">
    <xsl:variable name="defaults" select="$subst[@name = 'group']/*" />
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="$defaults" mode="copy-or-default">
        <xsl:with-param name="parent" select="." />
      </xsl:apply-templates>
      <!-- any elements that don't have a default AND any non-element children -->
      <xsl:apply-templates select="
        *[not(name() = $defaults/@name)] | node()[not(self::*)]
      " />
    </xsl:copy>
  </xsl:template>

  <!-- in "copy-or-default" mode, this checks the context parent
       and either copies the element from there or uses the default --> 
  <xsl:template match="subst:element" mode="copy-or-default">
    <xsl:param name="parent" />

    <xsl:choose>
      <xsl:when test="$parent and $parent/*[name() = current()/@name]">
        <xsl:apply-templates select="$parent/*[name() = current()/@name]" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="." />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

应用于此输入

<x>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <group>
      <body>0004</body>
      <!-- a comment! -->
      <foo>blah</foo>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <category>some text</category>
    <summary>some text</summary>
  </story>
</x>

产生

<x>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0004</body>
      <!-- a comment! -->
      <foo>blah</foo>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
  <story>
    <group>
      <section>0001</section>
      <head>0002</head>
      <body>0003</body>
    </group>
    <category>some text</category>
    <summary>some text</summary>
  </story>
</x>