当Input XML具有namespcaes时,XSLT2.0给出空输出

时间:2013-11-12 21:27:11

标签: xslt namespaces xslt-2.0

要求是在XML中查找重复元素(BaseName)并使用isDuplicate属性标记父元素(Account)。当输入XML RootElement没有名称空间时,XSL正常工作。当根元素具有命名空间时,我得到空对象。我不确定为什么命名空间导致XSL生成空输出。任何有助于获得正确输出的帮助都将非常感激。

使用NAMESPACE输入XML

 <?xml version="1.0"?>
    <objects xmlns="urn:s.sexmaple.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <Account>
            <Id>001A00F</Id>
            <RecordTypeId>012A00</RecordTypeId>
            <BaseName>EFGH</BaseName>
        </Account>
       <Account>
            <Id>001A0</Id>
            <RecordTypeId>012A0</RecordTypeId>
            <BaseName>ABCD</BaseName>
        </Account>
       <Account>
            <Id>001A</Id>
            <RecordTypeId>012A</RecordTypeId>
            <BaseName>ABCD</BaseName>
        </Account>
    </objects>

XSL

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
        <xsl:output method="xml"
                    version="1.0"
                    encoding="UTF-8"
                    indent="yes"/>
        <xsl:strip-space elements="*" />
        <xsl:template match="node()|@*">
            <xsl:copy copy-namespaces="no">
                <xsl:apply-templates select="node()|@*" />
            </xsl:copy>
    </xsl:template>
    <xsl:template match="/">
        <xsl:variable name="Accounts">
            <objects>
                <xsl:for-each select="//Account">
                    <xsl:sort select="BaseName" />
                    <xsl:apply-templates select="." />
                </xsl:for-each>
            </objects>
        </xsl:variable>        
        <xsl:variable name="unqentity">
            <objects>
                <xsl:for-each select="$Accounts/objects/Account">
                    <xsl:choose>
                        <xsl:when test="not(following-sibling::Account/BaseName=./BaseName) and not(preceding-sibling::Account/BaseName=./BaseName) ">
                            <xsl:copy-of select="." />
                        </xsl:when>
                        <xsl:otherwise>
                            <Account>
                                <xsl:attribute name="isDuplicate">yes</xsl:attribute>
                                <xsl:for-each select="child::*">
                                    <xsl:element name="{name()}">
                                        <xsl:copy-of select="@*|node()" />
                                    </xsl:element>
                                </xsl:for-each>
                            </Account>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each>
            </objects>
        </xsl:variable>
        <xsl:copy-of select="$unqentity" />
    </xsl:template>
</xsl:stylesheet>

当输入XML具有NAMESPACE时输出XML

 <?xml version="1.0" encoding="UTF-8"?>
    <objects/>
输入没有命名空间时

输出XML

<?xml version="1.0" encoding="UTF-8"?>
     <objects>
            <Account>
                <Id>001A00F</Id>
                <RecordTypeId>012A00</RecordTypeId>
                <BaseName>EFGH</BaseName>
            </Account>
           <Account isDuplicate="yes">
                <Id>001A0</Id>
                <RecordTypeId>012A0</RecordTypeId>
                <BaseName>ABCD</BaseName>
            </Account>
           <Account isDuplicate="yes">
                <Id>001A</Id>
                <RecordTypeId>012A</RecordTypeId>
                <BaseName>ABCD</BaseName>
            </Account>
        </objects>

2 个答案:

答案 0 :(得分:2)

当你有一个命名空间时,它意味着命名空间中的元素与没有命名空间的元素(或者实际上是不同名称空间中的元素)不同。

这意味着在XSLT中执行此操作...

 <xsl:for-each select="//Account">

您正在寻找没有名称空间的帐户元素,因此它与源XML中的帐户元素不匹配,后者位于有趣的标题为“urn”中:s.sexmaple.com“(我怀疑是拼写错误的)

当您使用XSLT2.0时,通过使用 xpath-default-namespace 为任何xpath表达式指定默认命名空间,有一种简单的方法可以解决此问题。通常情况下,这可能就足够了,但是通过在变量中创建新元素会让您稍微复杂一些,然后您可以选择这些元素。

<xsl:for-each select="$Accounts/objects/Account">

这意味着当您在 $ Accounts 变量中创建对象帐户元素时,它们也需要成为命名空间的一部分

要切入追逐,这就是 xsl:stylesheet 元素需要的样子

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" 
                xmlns="urn:s.sexmaple.com" 
                xpath-default-namespace="urn:s.sexmaple.com">

因此,xpath-default-namespace="urn:s.sexmaple.com"用于匹配源XML中的元素,而xmlns="urn:s.sexmaple.com"用于确保您在变量中创建的元素具有此命名空间,并且可以在以后进行匹配。 / p>

说了这么多,你的整个XSLT过于复杂了。您是否只是尝试将 IsDuplicate 属性添加到具有相同 BaseName 帐户元素?好吧,创建一个键来查找重复项,如此

<xsl:key name="account" match="Account" use="BaseName" />

然后你可以像这样查找重复项:

         <xsl:if test="key('account', BaseName)[2]">
            <xsl:attribute name="isDuplicate">Yes</xsl:attribute>
         </xsl:if>

试试这个XSLT,它应该给出相同的结果

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="urn:s.sexmaple.com">
    <xsl:output method="xml" indent="yes"/>

   <xsl:key name="account" match="Account" use="BaseName" />

   <xsl:template match="Account">
           <xsl:copy>
             <xsl:if test="key('account', BaseName)[2]">
                <xsl:attribute name="isDuplicate">Yes</xsl:attribute>
             </xsl:if>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
   </xsl:template>  

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

注意这只需要使用xpath-default-namespace,因为它不是创建全新的元素,只是复制现有元素(也可以复制它们的命名空间)。

答案 1 :(得分:0)

没有命名空间的输入XML工作的原因是具有命名空间的输入XML不是因为输入XML,而是因为XSLT样式表。

当您的XML文件具有默认命名空间时,需要在样式表本身中声明该命名空间。

例如,使用以下XML:

<test xmlns="test.xml.schema">
  <element>Content</element>
</test>

当我应用以下XSLT时:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:template match="/">
    <out>
       <namespace>None</namespace>
       <xsl:apply-templates />
    </out>
  </xsl:template>

  <xsl:template match="test">
    <test out="True">hello</test>
  </xsl:template>
  <xsl:template match="*"/>

</xsl:stylesheet>

输出只是:

<out><namespace>None</namespace></out>

<xsl:template match="test">在输入xml中的test元素上无法匹配,因为它实际上是test.xml.schema:test,而样式表中没有名称空间的匹配实际上是:test。因此不可能匹配。

但是,当我们只为输入文档添加命名空间时,请修改模板,如下所示:

<xsl:stylesheet xmlns:t="test.xml.schema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:template match="/">
    <out>
       <namespace>test.xml.schema</namespace>
       <xsl:apply-templates />
    </out>
  </xsl:template>

  <xsl:template match="t:test">
    <test out="True">hello</test>
  </xsl:template>
  <xsl:template match="*"/>

</xsl:stylesheet>    

输出变为:

<out xmlns:t="test.xml.schema">
  <namespace>test.xml.schema</namespace>
  <test out="True">hello</test>
</out>

重要的是要注意输入文档和XSL中的命名空间缩写不需要相同(例如,空白与“t”),但它们自己的命名空间:(例如,两个空白并且“t必须绑定到test.xml.schema)。

另请注意,using a default namespace in XSLT can be fraught with issues。所以最好在XSLT中使用声明的命名空间。