3

我是 XSLT 的新手,现在我有一个相当复杂的问题......我的输入文件看起来像

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<doc>
  <outerElement>
    <first>
      <textElement>Some Text</textElement>
    </first>
    <second>
      <textElement>Some Text</textElement>
    </second>
    <third>
      <textElement>Some Text</textElement>
    </third>
  </outerElement>
</doc>

“第二个”元素出现问题。在输入文件中,它可以是 3 种形式之一:

MISSING

<second>
  <textElement>Some Text</textElement>
</second>

<second missingCause="" />

在输出文件中,它应该像第二种形式一样插入。如果在 textElement 应该指示它是由模板插入之前它丢失了,这里重要的是它必须位于第二个位置,因为我想根据 xsd 模式验证它......

这是我当前的 XSL:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

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

  <!-- remove Elements with attribute deleteme -->
  <xsl:template match="outerElement/second[@missingCause='*']" />

  <!-- look if second is there. If not insert -->
  <xsl:template match="outerElement">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <xsl:if test="not(second)">
        <second>
        </second>
      </xsl:if>
    </xsl:copy>    
  </xsl:template>

  <!-- Insert Element second -->
  <xsl:template match="outerElement/second">
    <xsl:apply-templates select="node()|@*"/>
    <xsl:copy>
      <xsl:if test="not(textElement)">
        <textElement>Inserted by Template</textElement>
      </xsl:if>
    </xsl:copy>    
  </xsl:template>

</xsl:stylesheet> 

如果缺少“第二个”,我的输出文件只会获得一个元素“”,但它是空的,并且最后一个转换不会被应用。当我在文档中收到警告时,其他一切看起来都很好...

有人可以帮我将元素移动到它必须在的位置,以便它验证到模式并使其在所有三种情况下都能正常工作吗?

我认为我的方式似乎不是很好,最终会变得混乱,因为我有几个类似的元素要像这样插入/删除。

谢谢阅读 ;)

4

2 回答 2

4

这是一个通用解决方案,适用于任意数量的不同名称的子代outerElement以及它们之间的任何首选顺序

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pUncertainElName" select="'second'"/>

 <xsl:param name="pOrderedNames" select="'|first|second|third|'"/>

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

 <xsl:template match="outerElement">
  <xsl:variable name="vrtfFirstPass">
      <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
       <xsl:apply-templates select=
         "self::*[not(*[name() = $pUncertainElName])
                or
                 *[name()=$pUncertainElName and @missing-cause]]"
         mode="missing"/>
      </xsl:copy>
  </xsl:variable>

  <xsl:apply-templates select="ext:node-set($vrtfFirstPass)/*" mode="pass2"/>
 </xsl:template>

 <xsl:template match="*[@missing-cause]"/>

 <xsl:template match="*" mode="missing">
    <xsl:element name="{$pUncertainElName}">
        <textElement>Some Text</textElement>
    </xsl:element>
 </xsl:template>

 <xsl:template match="outerElement" mode="pass2">
  <xsl:copy>
   <xsl:apply-templates>
     <xsl:sort data-type="number" select=
     "string-length(substring-before($pOrderedNames,
                                     concat('|', name(), '|')
                                     )
                   )"/>
   </xsl:apply-templates>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时:

<doc>
    <outerElement>
        <first>
            <textElement>Some Text</textElement>
        </first>
        <second missing-cause="">
            <textElement>Some Text</textElement>
        </second>
        <third>
            <textElement>Some Text</textElement>
        </third>
    </outerElement>
</doc>

产生了想要的正确结果:

<doc>
   <outerElement>
      <first>
         <textElement>Some Text</textElement>
      </first>
      <second>
         <textElement>Some Text</textElement>
      </second>
      <third>
         <textElement>Some Text</textElement>
      </third>
   </outerElement>
</doc>

当它应用于此 XML 文档时

<doc>
    <outerElement>
        <first>
            <textElement>Some Text</textElement>
        </first>
        <third>
            <textElement>Some Text</textElement>
        </third>
    </outerElement>
</doc>

再次产生所需的正确结果

<doc>
   <outerElement>
      <first>
         <textElement>Some Text</textElement>
      </first>
      <second>
         <textElement>Some Text</textElement>
      </second>
      <third>
         <textElement>Some Text</textElement>
      </third>
   </outerElement>
</doc>

最后,当对这个 XML 文档应用相同的转换时:

<doc>
    <outerElement>
        <first>
            <textElement>Some Text</textElement>
        </first>
        <second>
            <textElement>Some Text</textElement>
        </second>
        <third>
            <textElement>Some Text</textElement>
        </third>
    </outerElement>
</doc>

再次产生同样想要的正确结果:

<doc>
   <outerElement>
      <first>
         <textElement>Some Text</textElement>
      </first>
      <second>
         <textElement>Some Text</textElement>
      </second>
      <third>
         <textElement>Some Text</textElement>
      </third>
   </outerElement>
</doc>

请注意

可以有任意数量的不同命名的孩子outerElement(不仅仅是三个),并且他们的顺序可能无法提前知道。

例如

鉴于此 XML 文档

<doc>
    <outerElement>
        <first>
            <textElement>Some Text</textElement>
        </first>
        <second missing-cause="">
            <textElement>Some Text</textElement>
        </second>
        <third>
            <textElement>Some Text</textElement>
        </third>
        <fourth>
            <textElement>Some Text</textElement>
        </fourth>
    </outerElement>
</doc>

这个顺序:第四,第二,第三,第一

我们只需要替换

 <xsl:param name="pOrderedNames" select="'|first|second|third|'"/>

 <xsl:param name="pOrderedNames" select="'|fourth|second|third|first|'"/>

现在产生了新的想要的结果

<doc>
   <outerElement>
      <fourth>
         <textElement>Some Text</textElement>
      </fourth>
      <second>
         <textElement>Some Text</textElement>
      </second>
      <third>
         <textElement>Some Text</textElement>
      </third>
      <first>
         <textElement>Some Text</textElement>
      </first>
   </outerElement>
</doc>

说明

  1. 这是一个两遍转换。

  2. 可能存在或不存在的元素的名称在外部/全局参数中指定$pUncertainElName。为了便于解释,我们将此元素称为second

  3. On the first pass, all children of outerElement with the exception of second that has the missing-cause attribute, are copied "as-is". If the second element was absent or had the missing-cause attribute, we output a new child of outerElement -- the exactly wanted second element.

  4. In the second pass we sort the children of the outerElement produced in the first pass, according to their priority, as specified in another external/global parameter, named $pOrderedNames (a name that is left of another name in this string, has higher priority)

于 2012-06-26T03:16:46.857 回答
3

我认为您误解了模板的工作原理。对于您需要完成的事情,似乎只需要一个模板(加上身份模板)。试用并反馈:

<xsl:template match="outerElement[not(second) or second[@missingCause='']]">
    <xsl:apply-templates select="@*|first"/>
    <second>
        <textElement>Inserted by Template</textElement>
    </second>
    <xsl:apply-templates select="third"/>
</xsl:template>
于 2012-06-25T17:29:38.593 回答