用xslt替换命名空间节点字符串值---我的xsl出了什么问题

时间:2015-04-09 17:23:11

标签: xslt

我正在尝试使用xslt替换命名空间字符串。

我在另一个xml文件中有源命名空间字符串和目标命名空间字符串,格式为" namespace source =" xxx"目标=" XXX"&#34 ;.我要转换的xml中的所有源命名空间字符串都应该更改为相应的目标值。只有元素才能被视为没有命名空间的属性。

我使用的是JDK默认的xalan xslt处理器,它支持xslt 1.0。如果可能,我想坚持使用xslt 1.0,但如果真的需要,我可以更换处理器并使用xslt 2.0。

例如,待转换输入xml:

<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3"> 
  <ElementA xmlns="http://ns4-old">
     <ElementB/>
     <ns2:elementD/>
  </ElementA>
</root>

输出xml应为(&#34; http://ns2-old&#34;更改为&#34; http://ns2-new&#34;和&#34; http://ns4-old& #34;更改为&#34; http://ns4-new&#34;):

<root xmlns="http://ns1" xmlns:ns2="http://ns2-new" xmlns:ns3="http://ns3"> 
  <ElementA xmlns="http://ns4-new">
     <ElementB/>
     <ns2:elementD/>
  </ElementA>
</root>

这是我的xsl不能正常工作:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:variable name="nsmapdoc" select="document('my-map-file')"/>
  <xsl:key name="nsmap" match="//namespace/@target" use="@source"/>

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

  <!-- process each element -->
  <xsl:template match="element()">
     <xsl:for-each select="namespace::*">
       <!-- for each namespace node of the element, save the string value-->
       <xsl:variable name="sourceuri"><xsl:value-of select="."/>
       </xsl:variable>

       <!-- get the target value for this namespace-->
       <xsl:variable name="targeturi">
         <xsl:for-each select="$nsmapdoc">
           <xsl:value-of select="key('nsmap', $sourceuri)"/>
         </xsl:for-each>
       </xsl:variable>

       <!-- if target value exists, replace the current namespace node string value with the target value-->
       <xsl:if test="$targeturi">
         <xsl:value-of select="$targeturi"/>
       </xsl:if>
     </xsl:for-each>
   </xsl:template>

</xsl:stylesheet>

我有几个问题:

  1. 对于执行复制的身份模板,为什么要做&#34;匹配=&#34; node()| @ *&#34;而不只是&#34;匹配=&#34;节点()&#34;?属性也是节点吗?

  2. 我不确定我是否正确获取了命名空间字符串值(例如&#34; http://ns1&#34;,&#34; http://ns2-old&#34;)对于元素。

  3. 我想我得到了正确的钥匙。但是,我不确定我是否正确使用了类型---看起来像targeturi变量不是一个字符串。如果key没有条目,那么查找条目的内容会返回什么?在我的例子中,我应该仅为在地图中有条目的命名空间替换命名空间值。

  4. 如何为命名空间节点编写新的字符串值?

  5. 我需要处理元素的每个命名空间节点。这是在内部定义变量的正确方法吗?

  6. 请帮忙看看我的xsl有什么问题。任何建议都表示赞赏。

1 个答案:

答案 0 :(得分:1)

我认为你在这里有两个,可能是三个不同的问题。

第一个问题是:如何使用&#34; map&#34;将元素从一个名称空间移动到另一个名称空间。源到目标命名空间。让我先回答这个问题。给出:

<强> XML

<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3"> 
  <ElementA xmlns="http://ns4-old">
     <ElementB/>
     <ns2:ElementD/>
  </ElementA>
</root>

<强> map.xml

<root>
    <namespace source="http://ns2-old" target="http://ns2-new"/>
    <namespace source="http://ns4-old" target="http://ns4-new"/>
</root>

以下样式表:

XSLT 1.0

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:param name="nsmapdoc" select="document('map.xml')"/>

<xsl:template match="*">
    <xsl:variable name="old-ns" select="namespace-uri()"/>
    <xsl:variable name="map-entry" select="$nsmapdoc/root/namespace[@source=$old-ns]"/>
    <xsl:variable name="new-ns">
        <xsl:choose>
            <xsl:when test="$map-entry">
                <xsl:value-of select="$map-entry/@target"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$old-ns"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:variable>
    <xsl:element name="{local-name()}" namespace="{$new-ns}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

将返回:

<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://ns1"> 
  <ElementA xmlns="http://ns4-new">
     <ElementB/>
     <ElementD xmlns="http://ns2-new"/>
  </ElementA>
</root>

注意

  1. ElementA及其子ElementB已从名称空间URI&#34; http://ns4-old&#34;移出到URI&#34; http://ns4-new&#34;;

  2. ElementD已从命名空间URI&#34; http://ns2-old&#34;移出到URI&#34; http://ns2-new&#34 ;;

  3. 已剥离ElementD的前缀;这是一种毫无意义的外观变化,它不应该给接收应用程序带来任何问题;

  4. 根元素保留在其原始名称空间中;

  5. 已废弃未使用的命名空间声明。

  6. 还要注意我们没有使用密钥来查找新的命名空间:在XSLT 1.0中使用跨文档的密钥非常尴尬,而我选择不这样做。


    第二个问题是如何复制未使用的命名空间声明。有几种可能的答案可供选择:

    1. 没有必要复制它们,因为它不用于任何东西;

    2. 无法复制它们;

    3. 可以通过复制源文档中的某些元素来复制它们;但复制元素也会复制其命名空间 - 所以只有当某个已知元素应该保留在其原始命名空间中时才能这样做。例如,如果您事先知道根元素不应该移动到另一个命名空间,则可以将另一个模板添加到样式表中:

                                 

    4. 得到这个结果:

      <?xml version="1.0" encoding="utf-8"?>
      <root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3"> 
        <ElementA xmlns="http://ns4-new">
           <ElementB/>
           <ElementD xmlns="http://ns2-new"/>
        </ElementA>
      </root>