XSLT 1.0按顺序插入多个新节点

时间:2017-09-14 20:00:07

标签: xml xslt-1.0

我需要将xml文件从一个版本转换为另一个版本,方法是将新元素及其子元素添加到旧版本,以使其与较新版本兼容。 (版本1 - >版本2)其中版本2具有一些新的必需元素。如果缺少元素或缺少子元素,我们需要添加默认值。顺序也很重要。
例如版本1:

<root>
  <a>
    <a1>A1</a1>
    <a2>A2</a2>
  </a>
  <b>
    <b1>B1</b1>
  </b>
</root>

第2版:

<root>
  <a>
    <a1>A1</a1>
    <a3>A3</a3>
    <a2>A2</a2>
  </a>
  <c>
    <c1>C1</c1>
    <c2>C2</c2>
  </c>
  <d>
    <d1>D1</d1>
    <d2>D2</d2>
    <d3>D3</d3>
  </d>
  <b>
    <b1>B1</b1>
  </b>
</root>

我尝试了几件事,但似乎遇到了我无法通过的路障。我为默认值创建变量,希望循环遍历它们并根据需要添加。

<xsl:variable name="defaultA">
  <a1>aOne<a1>
  <a2>aTwo<a2>
</xsl:variable>

 .
 .
 .
<xsl:variable name="defaultC">
  <c1>cOne<c1>
  <c2>cTwo<c2>
</xsl:variable>

<xsl:variable name="defaultB">
  <b1>bOne<b1>
  <b2>bTwo<b2>
</xsl:variable>


<xsl:template match="root">
 <xsl:variable name="defaults" select="document('')/*/xsl:variable[contains(@name,'default')]/*" />
 <xsl:variable name="defaultNodes" select="ext:node-set($defaults)"/>
 <xsl:copy>
     <xsl:for-each select="$defaultNodes">
        <xsl:copy-of select="node()[not(name() = name(current()))]"/>
     </xsl:for-each>
     <xsl:apply-templates select="node()|@*" />
 </xsl:copy>
</xsl:template>

这将添加节点,但它将命名空间添加到元素(我不想要),并且如果它们已经存在,将继续添加元素事件。

任何有助于我朝着正确方向前进的帮助都将不胜感激。提前谢谢。

1 个答案:

答案 0 :(得分:1)

最方便的方法在很大程度上取决于您的内容模型的外观,以及v2中是否存在任何元素,但在v1中是否可选。

根据你的说法,我猜是

  • v1中允许的每个元素在v2中都是合法的,应该保留。
  • v2中需要v1中的一些可选元素;如果它们存在于实例中,则应保留它们,如果它们不存在,则应提供默认值。
  • 内容模型是一个扁平的元素序列,没有重复,也没有子组。
  • v1中的元素序列是v2中的元素序列(即,有效v1实例中的所有元素都保留其在v2中的相对位置。)

如果这些猜测错了,您需要在答案中做出适当调整(您可能会考虑尝试让问题更清晰)。

有几个案例需要考虑:

  • v2中需要元素,v1中需要,可选或不存在。
  • 元素在v2中是可选的,在v1中是必需的,可选的或不存在。

作为示例,假设v1中所需的元素具有以“R”开头的名称,并且类似于v1中可选且不存在的元素具有以“O”和“A”开头的名称。 v1中所需的元素和可选元素的名称以“R”和“O”结尾。所以我们有六个案例,这里用元素AO,AR,OO,OR,RO,RR表示。 v1的内容模型为(OO?, OR?, RO, RR),v2的内容模型为(AO?, AR, OO?, OR, RO?, RR)

处理父元素的简单模板(我称之为P)看起来像这样(未经测试):

<xsl:template match="P">
  <!--* AO is optional and will never appear in v1 input.
      * So we do nothing. *-->

  <!--* AR is required and will never appear in v1 input.
      * So we inject it unconditionally. *-->
  <xsl:call-template name="default-AR"/>

  <!--* OO is optional and might appear in v1 input.
      * So we keep it if it's here. *-->
  <xsl:apply-templates select="OO"/>

  <!--* OR is required and might appear in v1 input.
      * We keep it if it's already present and supply a
      * default version if it's missing. *-->
  <xsl:apply-templates select="OR"/>
  <xsl:if test="not(OR)">
    <xsl:call-template name="default-OR"/>
  </xsl:if>

  <!--* RO is optional but will always appear in v1 input.
      * We keep it. *-->
  <xsl:apply-templates select="RO"/>

  <!--* RR is required in v1 and v2.  Just keep it. *-->
  <xsl:apply-templates select="RR"/>
</xsl:template>

如果您在六个月后阅读此样式表,您会发现元素OR的逻辑更容易使用xsl:choose,使用它而不是上面显示的代码。如果您发现apply-templates后跟if更容易理解,请使用它。

  <xsl:choose>
    <xsl:when test="OR">
      <xsl:apply-templates select="OR"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="default-OR"/>
    </xsl:otherwise>
  </xsl:choose>

如果内容模型不是平面序列,模板的逻辑可能会变得更加复杂;在一般情况下,不清楚从内容模型中具有更多元素类型和可能不同结构的从v1到v2的转换是可能的。 (你的假设是我猜测v1和v2内容模型都是扁平序列的主要原因。)