【问题标题】:XSLT2.0 giving empty output when Input XML is having namespcaes当输入 XML 具有命名空间时,XSLT2.0 给出空输出
【发布时间】:2013-11-12 21:27:10
【问题描述】:

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

输入带有命名空间的 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 有命名空间时输出 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>

【问题讨论】:

  • 您是否有理由不将命名空间添加到 XSL?还是您的问题是您不了解命名空间的工作原理?

标签: xslt namespaces xslt-2.0


【解决方案1】:

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

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

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

您正在寻找一个没有命名空间的 Account 元素,因此它与源 XML 中的 Account 元素不匹配,该元素位于有趣的标题“urn”中:s.sexmaple.com"(我怀疑是拼写错误)

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

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

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

切入正题,这就是您的 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" 用于确保您在变量中创建的元素具有此命名空间,并且可以在以后进行匹配。

说了这么多,您的整个 XSLT 相当复杂。您是否只是尝试将 IsDuplicate 属性添加到具有相同 BaseNameAccount 元素?好吧,创建一个键来查找重复项,就像这样

<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,因为它不会创建全新的元素,只是复制现有元素(它们的命名空间也会被复制)。

【讨论】:

    【解决方案2】:

    没有命名空间的输入 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>
    

    &lt;xsl:template match="test"&gt; 无法匹配输入 xml 中的测试元素,因为它实际上是 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 中使用声明的命名空间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-10
      • 2021-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多