xslt:当根元素事先不知道时,在root上保留名称空间声明

时间:2011-06-17 20:07:51

标签: xslt-2.0

我有xml文档,这些文档遵循一个模式,其中大多数已定义的元素都被允许作为有效实例的根。我还有几个xslt的v2.0,它以各种方式翻译它(把它变成普通形式,一个紧凑的形式,一个不同的方言,......)这些xslt都是基于身份变换,添加了模板以实现所需修改。问题在于命名空间属性的增加,因为有些元素来自默认命名空间之外。

我已经尝试了在根元素上插入命名空间的推荐过程,但我似乎无法做到正确。问题是: 1.转换可能会改变名称,有时也会改变根元素的内容,所以我仍然需要每个全局元素的模板,因为我不知道哪个是root,我不能只是插入必要时的名称空间元素(我不知道特定文档需要它们的位置。 2.我想把它实现为多遍,或者只是一个独立的xslt,因为我想要几个不同的xslts相同的结果。在这种情况下,我需要的是一个标识转换,它从文档中的所有元素中获取所有名称空间和前缀,并将它们插入到根目录中。我希望,这会自动从子节点中删除命名空间属性吗?但是,我尝试了以下

<?xml version="1.0" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:output method="xml" indent="yes"/>
    <xsl:template name="start" match="/">
        <xsl:copy>
            <xsl:for-each select="*">
                <xsl:copy>
                    <xsl:for-each select="descendant::*">
                        <xsl:call-template name="add-ns">
                            <xsl:with-param name="ns-namespace">
                                <xsl:value-of select="namespace-uri()"/>
                            </xsl:with-param>
                            <xsl:with-param name="ns-prefix">
                                <xsl:value-of
                                    select=" prefix-from-QName( QName(namespace-uri(),name()))"/>
                            </xsl:with-param>
                        </xsl:call-template>
                    </xsl:for-each>
                    <xsl:apply-templates select="node() | @*"/>
                </xsl:copy>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    <xsl:template name="add-ns">
        <xsl:param name="ns-prefix" select="'x'"/>
        <xsl:param name="ns-namespace" select="'someNamespace'"/>
        <xsl:namespace name="{$ns-prefix}" select="$ns-namespace"/>
    </xsl:template>
    <xsl:template match="node()|@* ">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

这适用于元素上出现的所有前缀,但它不会捕获属性的前缀。这是一份测试文件:

<RuleML xmlns="http://www.ruleml.org/0.91/xsd">
   <Assert textiri="xy&gt;z">
      <Importation xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="abc"
                   textiri="urn:common-logic:demo1"
                   xlink:href="http://common-logic.org/x&gt;cl/demos.xml"/>
      <a:anything xmlns:a="http://anything.org"
                  xmlns:xlink="http://www.w3.org/1999/xlink"/>
   </Assert>
</RuleML>

我希望它能产生:

<RuleML xmlns="http://www.ruleml.org/0.91/xsd" xmlns:a="http://anything.org" xmlns:xlink="http://www.w3.org/1999/xlink" >
   <Assert textiri="xy&gt;z">
      <Importation xml:id="abc"
                   textiri="urn:common-logic:demo1"
                   xlink:href="http://common-logic.org/x&gt;cl/demos.xml"/>
      <a:anything/>
   </Assert>
</RuleML>

但我得到了

<RuleML xmlns="http://www.ruleml.org/0.91/xsd" xmlns:a="http://anything.org">
   <Assert textiri="xy&gt;z">
      <Importation xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="abc"
                   textiri="urn:common-logic:demo1"
                   xlink:href="http://common-logic.org/x&gt;cl/demos.xml"/>
      <a:anything xmlns:xlink="http://www.w3.org/1999/xlink"/>
   </Assert>
</RuleML>

塔拉

2 个答案:

答案 0 :(得分:3)

以下是否符合您的要求?

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs">

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

  <xsl:template match="/*">
    <xsl:copy>
      <xsl:copy-of select="descendant::*/namespace::*"/>
      <xsl:apply-templates select="@* , node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

使用Saxon 9.3,它似乎可以对您发布的样本执行任务。 但是,如果在不同的默认命名空间中有多个元素,或者在不同的命名空间中有多个元素但使用相同的前缀,我不确定你想要做什么。例如

<root xmlns="http://example.com/ns1">
  <foo xmlns="http://example.com/ns2">
    <pf:bar xmlns:pf="http://example.com/ns3">
      <pf:foobar xmlns:pf="http://example.com/ns4"/>
    </pf:bar>
  </foo>
</root>

Saxon只报告错误

Error at xsl:copy-of on line 15 of test2011061801Xsl2.xsl:
  XTDE0430: Cannot create two namespace nodes with the same prefix mapped to different URIs
  (prefix="", URI=http://example.com/ns2, URI=http://example.com/ns1)
  in built-in template rule

[编辑] 如果您不希望报告错误,可以尝试实施策略以尽可能地提取命名空间节点,但要避免任何冲突。这可以通过for-each-group来完成,如下面的示例XSLT 2.0:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs">

  <xsl:template match="@* | text() | processing-instruction() | comment()">
    <xsl:copy/>
  </xsl:template>

  <xsl:template match="*">
    <xsl:copy copy-namespaces="no">
      <xsl:for-each-group select="descendant-or-self::*/namespace::*" group-by="local-name()">
        <xsl:copy-of select="."/>
      </xsl:for-each-group>
      <xsl:apply-templates select="@* , node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

输入为

<root xmlns="http://example.com/ns1">
  <foo xmlns="http://example.com/ns2">
    <pf:bar xmlns:pf="http://example.com/ns3">
      <pf:foobar xmlns:pf="http://example.com/ns4"/>
    </pf:bar>
  </foo>
</root>

Saxon 9.3输出

<?xml version="1.0" encoding="UTF-8"?><root xmlns="http://example.com/ns1" xmlns:pf="http://example.com/ns3">
  <foo xmlns="http://example.com/ns2">
    <pf:bar>
      <pf:foobar xmlns:pf="http://example.com/ns4"/>
    </pf:bar>
  </foo>
</root>

答案 1 :(得分:0)

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="*:RuleML">
        <xsl:copy>
        <xsl:for-each select="descendant::node()">
            <xsl:choose>
                <xsl:when test="self::text()"/>

                <xsl:otherwise>
                    <xsl:for-each select="namespace::node()">
                        <xsl:copy-of select="."/>
                    </xsl:for-each>        
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>
            <xsl:apply-templates select="(node() | @*) except namespace::node()"/>
        </xsl:copy>
    </xsl:template>

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