如何通过param在XSLT中设置命名空间名称

时间:2013-12-13 10:23:19

标签: java xml xslt xslt-2.0

我需要在运行时在XSLT样式表中设置命名空间。 这是我的XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tdns="{$myNS}">
    <xsl:output method="xml" encoding="iso-8859-1" />

    <xsl:param name="myNS" required="yes" />

    <xsl:template match="*">

        <xsl:choose>
            <xsl:when test="name(.)='bugs'">
                <xsl:element name="tdns:bugs">

                    <xsl:for-each select="*">
                        <xsl:apply-templates select="." />
                    </xsl:for-each>
                </xsl:element>
            </xsl:when>
            <xsl:when test="name(.)='bug'">

                <xsl:element name="tdns:bug">
                    <xsl:for-each select="*">
                        <xsl:choose>
                            <xsl:when test="name(.)='Device' ">
                                <xsl:choose>
                                    <xsl:when test="string-length(../tdns:Device_Tmp) &gt; 0 ">
                                        <xsl:element name="tdns:Device">

                                            <xsl:value-of select="../tdns:Device_Tmp" />
                                        </xsl:element>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:element name="tdns:Device">
                                            <xsl:value-of select="." />
                                        </xsl:element>
                                    </xsl:otherwise>
                                </xsl:choose>
                                <xsl:element name="tdns:ShowParamValue">
                                    <xsl:value-of select="$myNS" />
                                </xsl:element>
                            </xsl:when>

                        </xsl:choose>
                    </xsl:for-each>
                </xsl:element>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

输入XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<bugs xmlns="http://my.com">
    <bug>
        <Device>
            <![CDATA[Dev1]]>
        </Device>
        <Device_Tmp>
            <![CDATA[Dev_tmp1]]>
        </Device_Tmp>
    </bug>
</bugs>

Java代码

TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer(new StreamSource(
        new File(xsl)));
transformer.setParameter("myNS", "http://my.com");
transformer.transform(new StreamSource(new File(
        xml)),
        new StreamResult(System.out));

输出XML

<?xml version="1.0" encoding="iso-8859-1"?>
<tdns:bugs xmlns:tdns="http://my.com">
    <tdns:bug>
        <tdns:Device>
            Dev1
        </tdns:Device>
        <tdns:ShowParamValue>http://my.com</tdns:ShowParamValue>
    </tdns:bug>
</tdns:bugs> 

如您所见,XSLT不知道任何../tdns:Device_Tmp节点,但如果我设置了静态命名空间:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tdns="http://my.com">
...

然后,结果是OK:

<?xml version="1.0" encoding="iso-8859-1"?>
<tdns:bugs xmlns:tdns="http://my.com">
    <tdns:bug>
        <tdns:Device>
            Dev_tmp1
        </tdns:Device>
        <tdns:ShowParamValue>http://my.com</tdns:ShowParamValue>
    </tdns:bug>
</tdns:bugs>

请告诉我我做错了什么。

2 个答案:

答案 0 :(得分:1)

命名空间声明不是属性,并且不被视为属性值模板(实际上它们由解析样式表文档的XML解析器处理,然后才能到达XSLT处理器附近)。

您必须使用<xsl:element>创建具有namenamespace属性的元素,以便能够动态指定命名空间。为了匹配元素,最简单的方法可能是完全忽略命名空间(因为你在XSLT 2.0中,*:localname)很容易。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="iso-8859-1" />

    <xsl:param name="myNS" required="yes" />

    <!-- this will cover most things, including a Device that doesn't have a
         sibling Device_Tmp -->
    <xsl:template match="*">
      <xsl:element name="tdns:{local-name()}" namespace="{$myNS}">
        <xsl:apply-templates />
      </xsl:element>
    </xsl:template>

    <!-- delete Device_Tmp -->
    <xsl:template match="*:Device_Tmp" />

    <!-- special case for Device elements that have a non-empty sibling
         Device_Tmp -->
    <xsl:template match="*:Device[string-length(../*:Device_Tmp) > 0]"
                  priority="2">
      <xsl:element name="tdns:{local-name()}" namespace="{$myNS}">
        <xsl:value-of select="../*:Device_Tmp" />
      </xsl:element>
    </xsl:template>

    <!-- add debugging element to bug -->
    <xsl:template match="*:bug">
      <xsl:element name="tdns:{local-name()}" namespace="{$myNS}">
        <xsl:apply-templates />
        <xsl:element name="tdns:ShowParamValue" namespace="{$myNS}">
          <xsl:value-of select="$myNS" />
        </xsl:element>
      </xsl:element>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:0)

看起来输入节点不在http://my.com命名空间中。你可以看到这个,因为name(。)='bugs'似乎返回true。 name()函数返回一个扩展的QName,它应该包含命名空间(而不是local-name())。

我不确定为什么会出现这种情况,但也尝试在输入xml中使用前缀。