Find all namespace declarations in an XML document - xPath 1.0 vs xPath 2.0

后端 未结 4 2066
渐次进展
渐次进展 2021-02-03 14:34

As part of a Java 6 application, I want to find all namespace declarations in an XML document, including any duplicates.

Edit: Per Martin\'s request, h

相关标签:
4条回答
  • 2021-02-03 15:25

    I think this will get all namespaces, without any duplicates:

    for $i in 1 to count(//namespace::*) return 
    if (empty(index-of((//namespace::*)[position() = (1 to ($i - 1))][name() = name((//namespace::*)[$i])], (//namespace::*)[$i]))) 
    then (//namespace::*)[$i] 
    else ()
    
    0 讨论(0)
  • 2021-02-03 15:27

    As the previous thread indicates, //namespace::* will return all the namespace nodes, of which there are 16, according to both the XPath 1.0 and XPath 2.0 implementations. It doesn't surprise me if you've found an implementation that doesn't implement the spec correctly.

    Finding all the namespace declarations (as distinct from namespace nodes) is not in general possible with either XPath 1.0 or XPath 2.0, because the following two documents are considered equivalent at the data model level:

    document A:

    <a xmlns="one">
      <b/>
    </a> 
    

    document B:

    <a xmlns="one">
      <b xmlns="one"/>
    </a>
    

    But if we define a "significant namespace declaration" to be a namespace that is present on a child element but not on its parent, then you could try this XPath 2.0 expression:

    for $e in //* return
      for $n in $e/namespace::* return
         if (not(some $p in $n/../namespace::* satisfies ($p/name() eq $e/name() and string($p) eq string($n)))) then concat($e/name(), '->', $n/name(), '=', string($n)) else ()
    
    0 讨论(0)
  • 2021-02-03 15:31

    To find all namespace declarations, I applied this xPath statement to the XML document using xPath 1.0:

    //namespace::* It finds 4 namespace declarations, which is what I expect (and desire):
    
    /root[1]/@xmlns:att - attribute.com
    /root[1]/@xmlns:ele - element.com 
    /root[1]/@xmlns:txt - textnode.com 
    /root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace
    

    You are using a non-compliant (buggy) XPath 1.0 implementation.

    I get different and correct results with all XSLT 1.0 processors I have. This transformation (just evaluating the XPath expression and printing one line for each selected namespace node):

    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/">
         <xsl:for-each select="//namespace::*">
           <xsl:value-of select="concat(name(), ': ', ., '&#xA;')"/>
         </xsl:for-each>
     </xsl:template>
    </xsl:stylesheet>
    

    when applied on the provided XML document:

    <root xmlns:ele="element.com" xmlns:att="attribute.com" xmlns:txt="textnode.com">
        <ele:one>a</ele:one>
        <two att:c="d">e</two>
        <three>txt:f</three>
    </root>
    

    produces a correct result:

    xml: http://www.w3.org/XML/1998/namespace
    ele: element.com
    att: attribute.com
    txt: textnode.com
    xml: http://www.w3.org/XML/1998/namespace
    ele: element.com
    att: attribute.com
    txt: textnode.com
    xml: http://www.w3.org/XML/1998/namespace
    ele: element.com
    att: attribute.com
    txt: textnode.com
    xml: http://www.w3.org/XML/1998/namespace
    ele: element.com
    att: attribute.com
    txt: textnode.com
    

    with all of these XSLT 1.0 and XSLT 2.0 processors:

    MSXML3, MSXML4, MSXML6, .NET XslCompiledTransform, .NET XslTransform, Altova (XML SPY), Saxon 6.5.4, Saxon 9.1.07, XQSharp.

    Here is a short C# program that confirms the number of nodes selected in .NET is 16:

    namespace TestNamespaces
    {
        using System;
        using System.IO;
        using System.Xml.XPath;
    
        class Test
        {
            static void Main(string[] args)
            {
                string xml =
    @"<root xmlns:ele='element.com' xmlns:att='attribute.com' xmlns:txt='textnode.com'>
        <ele:one>a</ele:one>
        <two att:c='d'>e</two>
        <three>txt:f</three>
    </root>";
                XPathDocument doc = new XPathDocument(new StringReader(xml));
    
                double count = 
                  (double) doc.CreateNavigator().Evaluate("count(//namespace::*)");
    
                Console.WriteLine(count);
            }
        }
    }
    

    The result is:

    16.

    UPDATE:

    This is an XPath 2.0 expression that finds just the "distinct" namespace nodes and produces a line of name - value pairs for each of them:

    for $i in distinct-values(
                 for $ns in //namespace::*
                   return
                      index-of(
                               (for $x in //namespace::*
                                 return
                                    concat(name($x), ' ', string($x))
    
                                ),
                                concat(name($ns), ' ', string($ns))
                              )
                              [1]
                                                      )
      return
        for $x in (//namespace::*)[$i]
         return
            concat(name($x), ' :', string($x), '&#xA;')
    
    0 讨论(0)
  • 2021-02-03 15:37

    Here are my results using the XPath 1.0 implementations of .NET's XPathDocument (XSLT/XPath 1.0 data model), XmlDocument (DOM data model) and MSXML 6's DOM; the test code run against your sample XML document is

        Console.WriteLine("XPathDocument:");
        XPathDocument xpathDoc = new XPathDocument("../../XMLFile4.xml");
        foreach (XPathNavigator nav in xpathDoc.CreateNavigator().Select("//namespace::*"))
        {
            Console.WriteLine("Node type: {0}; name: {1}; value: {2}.", nav.NodeType, nav.Name, nav.Value);
        }
        Console.WriteLine();
    
        Console.WriteLine("DOM XmlDocument:");
        XmlDocument doc = new XmlDocument();
        doc.Load("../../XMLFile4.xml");
        foreach (XmlNode node in doc.SelectNodes("//namespace::*"))
        {
            Console.WriteLine("Node type: {0}; name: {1}; value: {2}.", node.NodeType, node.Name, node.Value);
        }
        Console.WriteLine();
    
    
        Console.WriteLine("MSXML 6 DOM:");
        dynamic msxmlDoc = Activator.CreateInstance(Type.GetTypeFromProgID("Msxml2.DOMDocument.6.0"));
        msxmlDoc.load("../../XMLFile4.xml");
        foreach (dynamic node in msxmlDoc.selectNodes("//namespace::*"))
        {
            Console.WriteLine("Node type: {0}; name: {1}; value: {2}.", node.nodeType, node.name, node.nodeValue);
        }
    

    and its output is

    XPathDocument:
    Node type: Namespace; name: txt; value: textnode.com.
    Node type: Namespace; name: att; value: attribute.com.
    Node type: Namespace; name: ele; value: element.com.
    Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.
    Node type: Namespace; name: txt; value: textnode.com.
    Node type: Namespace; name: att; value: attribute.com.
    Node type: Namespace; name: ele; value: element.com.
    Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.
    Node type: Namespace; name: txt; value: textnode.com.
    Node type: Namespace; name: att; value: attribute.com.
    Node type: Namespace; name: ele; value: element.com.
    Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.
    Node type: Namespace; name: txt; value: textnode.com.
    Node type: Namespace; name: att; value: attribute.com.
    Node type: Namespace; name: ele; value: element.com.
    Node type: Namespace; name: xml; value: http://www.w3.org/XML/1998/namespace.
    
    DOM XmlDocument:
    Node type: Attribute; name: xmlns:txt; value: textnode.com.
    Node type: Attribute; name: xmlns:att; value: attribute.com.
    Node type: Attribute; name: xmlns:ele; value: element.com.
    Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
    ce.
    Node type: Attribute; name: xmlns:txt; value: textnode.com.
    Node type: Attribute; name: xmlns:att; value: attribute.com.
    Node type: Attribute; name: xmlns:ele; value: element.com.
    Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
    ce.
    Node type: Attribute; name: xmlns:txt; value: textnode.com.
    Node type: Attribute; name: xmlns:att; value: attribute.com.
    Node type: Attribute; name: xmlns:ele; value: element.com.
    Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
    ce.
    Node type: Attribute; name: xmlns:txt; value: textnode.com.
    Node type: Attribute; name: xmlns:att; value: attribute.com.
    Node type: Attribute; name: xmlns:ele; value: element.com.
    Node type: Attribute; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespa
    ce.
    
    MSXML 6 DOM:
    Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
    Node type: 2; name: xmlns:ele; value: element.com.
    Node type: 2; name: xmlns:att; value: attribute.com.
    Node type: 2; name: xmlns:txt; value: textnode.com.
    Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
    Node type: 2; name: xmlns:ele; value: element.com.
    Node type: 2; name: xmlns:att; value: attribute.com.
    Node type: 2; name: xmlns:txt; value: textnode.com.
    Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
    Node type: 2; name: xmlns:ele; value: element.com.
    Node type: 2; name: xmlns:att; value: attribute.com.
    Node type: 2; name: xmlns:txt; value: textnode.com.
    Node type: 2; name: xmlns:xml; value: http://www.w3.org/XML/1998/namespace.
    Node type: 2; name: xmlns:ele; value: element.com.
    Node type: 2; name: xmlns:att; value: attribute.com.
    Node type: 2; name: xmlns:txt; value: textnode.com.
    

    So it is certainly not an XPath 1.0 versus XPath 2.0 problem. I think the problem you see is a shortcoming of mapping the XPath data model with namespace nodes against the DOM model with attribute nodes. Someone more familiar with the Java XPath API needs to tell you whether the behaviour you see is correctly implementation dependent as the API specification is not precise enough for the case of mapping the XPath namespace axis to the DOM model or whether it is a bug.

    0 讨论(0)
提交回复
热议问题