2

The requirement is to find the duplicate element(BaseName) in XML and marked the parent element(Account) with isDuplicate attribute. The XSL is working correctly when the input XML RootElement has no namespaces. When the root element has namespace then I get empty object. I am not sure why the namespace is causing XSL to generate empty output. Any help to get the right output would be greatly appreciated.`

Input XML WITH NAMESPACE

 <?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>

Output XML WHEN INPUT XML HAS NAMESPACE

 <?xml version="1.0" encoding="UTF-8"?>
    <objects/>

Output XML when Input has no Namespaces

<?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>
4

2 回答 2

2

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

这意味着当您在 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变量中创建对象Account元素时,它们也需要成为命名空间的一部分。

切入正题,这就是您的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属性添加到具有相同BaseName的Account元素?好吧,创建一个键来查找重复项,就像这样

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

于 2013-11-12T22:42:12.153 回答
0

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

另请注意,在 XSLT 中使用默认名称空间可能会带来很多问题。所以最好在 XSLT 中使用声明的名称空间。

于 2013-11-12T22:38:41.430 回答