【问题标题】:XSLT 3.0:仅当输入元素有数据时才创建元素
【发布时间】:2022-01-14 11:34:24
【问题描述】:

我们在内部使用XSLT 将单个输入模式映射到大量不同的输出模式。大多数使用这些模式的服务器在空元素上返回错误,因此空元素不能出现在输出中。在许多情况下,输入中的一条数据将简单地映射到输出中的一条数据,可能需要进行较小的转换,例如:


<!-- Input -->
<ourns:DateCreated>2021-12-09</ourns:DateCreated>

<!-- Output -->
<otherns:CreatedDt>2021-12-09<otherns:CreatedDt>

用于此的 XSLT 很简单,即使有“无空元素”要求:

<xsl:if test="ourns:DateCreated != ''">
  <otherns:CreatedDt>
    <xsl:value-of select="ourns:DateCreated/text()"/>
  </otherns:CreatedDt>
</xsl:if>

但是,当您在数百个模式中映射数千个元素时,将所有内容包装在 &lt;xsl:if/&gt; 中的这种业务会变得很烦人。你可以添加一个函数,比如:

<xsl:function name="ourfn:createElementIfData">
  <xsl:param name="tag" as="xs:string"/>
  <xsl:param name="data" as="xs:string"/>
  <xsl:if test="$data != ''">
    <xsl:element name="{$tag}"><xsl:value-of select="$data"/></xsl:element>
  </xsl:if>
</xsl:function>
...
<xsl:sequence select="ourfn:createElementIfData('otherns:CreatedDt', ourns:DateCreated)"/>

但是这个函数只有在声明了两个命名空间的样式表中才能工作。如果你想分享它(就像你可能会想要这样一个通用功能),你最终需要要么

  1. 在共享样式表中声明所有可能的otherns,或
  2. 在每次调用时传入完全限定的命名空间,

两者都感觉不对。

这似乎是一个常见的用例,我觉得必须有一种简单的方法来做到这一点。我错过了什么?

【问题讨论】:

    标签: xslt xml-namespaces xslt-3.0


    【解决方案1】:

    你可以这样定义你的基本规则:

    <xsl:template match="ourns:DateCreated" 
                  mode="copySimpleElement">
      <otherns:CreatedDt>{.}</otherns:CreatedDt>
    </xsl:template>
    

    然后为空元素覆盖它:

    <xsl:template match="*[. = '']" 
                  mode="copySimpleElement" 
                  priority="20"/>
    

    然后您只需在适当的模式下将模板应用到相关元素。

    【讨论】:

      【解决方案2】:

      您没有显示任何上下文,但可能 &lt;xsl:template match="ourns:*[not(has-children())]"/&gt; 足以防止对没有内容的元素进行任何处理,并且添加 &lt;xsl:template match="ourns:DateCreated[has-children()]" expand-text="yes"&gt;&lt;otherns:CreatedDt&gt;{.}&lt;/otherns:CreatedDt&gt;&lt;/xsl:template&gt; 足以将非空元素映射到所需的输出元素。

      当然,&lt;xsl:template match="ourns:*[not(has-children())]"/&gt; 可以设置为 &lt;xsl:template match="*[not(has-children())]"/&gt;,前提是规则可以应用于来自任何命名空间的输入元素,或者可以采用 &lt;xsl:template match="ourns:*[not(has-children())] | ourns2:*[not(has-children())]"/&gt; 的一系列模式。

      以上所有假设您正在通过其他模板处理这些节点,例如身份转换&lt;xsl:mode on-no-match="shallow-copy"/&gt;

      如果您想采用函数方法,我会检查您是否可以传入xs:QName

      <xsl:function name="ourfn:createElementIfData">
        <xsl:param name="node-name" as="xs:QName"/>
        <xsl:param name="data" as="xs:string"/>
        <xsl:if test="$data != ''">
          <xsl:element name="{$node-name}" namespace="{namespace-uri-from-QName($node-name)}"><xsl:value-of select="$data"/></xsl:element>
        </xsl:if>
      </xsl:function>
      

      并使用例如&lt;xsl:sequence select="ourfn:createElementIfData(QName('http://yourothernamespace/', 'otherns:CreatedDt'), ourns:DateCreated)"/&gt;.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-21
        • 1970-01-01
        • 2023-03-27
        • 2022-11-28
        • 2023-03-14
        • 1970-01-01
        • 2018-04-28
        相关资源
        最近更新 更多