2012-04-18 3 views
3

Как часть приложения Java 6, я хочу найти все объявления пространства имен в документе XML, включая любые дубликаты.Найти все объявления пространства имен в документе XML - xPath 1.0 vs xPath 2.0

Редактировать: По желанию Мартина, вот код Java Я использую:

XPathFactory xPathFactory = XPathFactory.newInstance(); 
XPath xPath = xPathFactory.newXPath(); 
XPathExpression xPathExpression = xPathExpression = xPath.compile("//namespace::*"); 
NodeList nodeList = (NodeList) xPathExpression.evaluate(xmlDomDocument, XPathConstants.NODESET); 

Предположим, что я это XML документ:

<?xml version="1.0" encoding="UTF-8"?> 
<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> 

Чтобы найти все объявления пространств имен, я применил этот xPath для XML-документа с использованием xPath 1.0:

//namespace::* 

Он находит 4 объявления пространств имен, которые я ожидаю (и желание):

/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 

Но если я изменяю с помощью XPath 2.0, то я получаю 16 объявлений пространств имен (каждый из предыдущих деклараций 4 раз), что не то, что я ожидал (или желания):

/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace 
/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 
/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 
/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 
/root[1]/@xmlns:att - attribute.com 
/root[1]/@xmlns:ele - element.com 
/root[1]/@xmlns:txt - textnode.com 

Эта же разница заметна даже тогда, когда я использую не-сокращенный вариант заявления XPATH:

/descendant-or-self::node()/namespace::* 

И это видно на различных анализаторах XML (LIBXML, MSXML.NET, Saxon), проверенных в oXygen. (Edit:. Как я уже потом в комментариях, это утверждение не верно, хотя я думал, что я был тестирования различных XML-парсеров, я на самом деле не было.)

Вопрос № 1: Почему разница от xPath 1.0 до xPath 2.0?

Вопрос №2: Возможно ли получение/получение желаемых результатов с использованием xPath 2.0?

Подсказка: Использование distinct-values() функции в XPath 2.0 будет не вернуть желаемые результаты, так как я хочу все объявления пространства имен, даже если одно пространство имен дважды объявлено. Например, рассмотрим этот XML-документ:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <bar:one xmlns:bar="http://www.bar.com">alpha</bar:one> 
    <bar:two xmlns:bar="http://www.bar.com">bravo</bar:two> 
</root> 

Желаемый результат:

/root[1]/@xmlns:xml - http://www.w3.org/XML/1998/namespace 
/root[1]/bar:one[1]/@xmlns:bar - http://www.bar.com 
/root[1]/bar:two[1]/@xmlns:bar - http://www.bar.com 
+0

Джеймс, пожалуйста, покажите нам код нахождения пространства имен «заявления». В моем понимании XPath '// namespace :: *' находит все узлы пространства имен, которые отличаются от декларации пространства имен, поскольку узлы пространства имен существуют на каждый элемент узла и не разделяются между узлами. Таким образом, XML-документ с четырьмя узлами элементов, где есть три объявления пространства имен в корневом элементе, путь должен найти четыре узла пространства имен для каждого из четырех элементов. Насколько мне известно, это должно быть одинаковым между XPath 1.0 и 2.0. Также такая запись, как '/ root [1]/@ xmlns: txt', довольно вводит в заблуждение. –

+0

Обозначение/root [1]/@ xmlns: txt происходит от oXygen. Это их представление узлов в нодлисте, что прекрасно. –

+0

Java-код, добавленный выше. Довольно стандартный материал. Спасибо за объяснение. –

ответ

7

Я думаю, что это будет получить все пространства имен, без дублей:

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

Вот оно! Этот xPath 2.0 найдет все объявления пространств имен, и он работает на обоих примерах, которые я дал в моем OP. Элегантность этого подхода заключается в том, что процессы пространств имен являются последовательностями. Молодец, @Roger. –

4

Чтобы найти все объявления пространств имен, я применил Xpath заявление документ XML с помощью 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 

Вы ар e с использованием несовместимой (багги) реализации XPath 1.0.

Я получаю разные и правильные результаты со всеми процессорами XSLT 1.0, которые у меня есть.Это преобразование (только вычисление выражения XPath и печать на одну строку для каждого выбранного узла пространства имен):

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

при нанесении на прилагаемом документе 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> 

производит правильный результат :

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 

со всеми этими XSLT 1.0 и XSLT 2.0 процессоры:

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

Вот короткий С # программа, которая подтверждает количество узлов, выбранных в .NET 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); 
     } 
    } 
} 

Результат:

16.

ОБНОВЛЕНИЕ:

Это 2.0 выражение XPath, который находит только «отдельные» узлы пространства имен и производит линию название - пар значений для каждого из них:

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

Я получаю 4 узла, когда: 1) Я использую парсер по умолчанию в Java 6, используя API xPath. 2) Я применяю xPath (как 1.0) с использованием XSV, LIBXML, MSXML4.0, MSXML.NET и Saxon-EE в oXygen 12.1. 3) Я использую ваш XSLT с Xalan (также в oXygen). Я получаю 16, когда я использую ваш XSLT с различными вкусами саксонского (в oXygen). Я не понимаю, почему я получаю разные ответы с Саксоном. Должно быть что-то простое, что мне не хватает ... –

+0

Я не склонен полагать, что парсер Java 6, MSXML.NET и Saxon-EE являются «несовместимыми (глючными) версиями XPath 1.0». Должно быть что-то еще ... –

+0

@ james.garriss: Как я уже сказал, я получаю тот же результат с использованием MSXML и .NET - эти процессоры XSLT не имеют отдельной/собственной оценки XPath - они используют доступный XPath двигатель. Таким образом, в вашем коде есть что-то, что приводит к выбору меньшего количества узлов. Когда у меня будет время, я добавлю пример кода C#, показывая, что выбрано 16 узлов. –

0

Вот мои результаты с использованием реализаций XPath 1.0 .NET XPathDocument (модель данных XSLT/XPath 1.0), XmlDocument (модель данных DOM) и DOM MSXML 6; тестовый код работать против вашего образца документа XML является

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); 
    } 

и его выход

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. 

Так что, конечно, не XPath 1.0 по сравнению с XPath 2.0 проблемы. Я думаю, что проблема, которую вы видите, является недостатком в сопоставлении модели данных XPath с узлами пространства имен с моделью DOM с узлами атрибутов. Кто-то, более знакомый с API Java XPath, должен сказать вам, правильно ли зависит поведение, которое вы видите, поскольку спецификация API недостаточно точна для случая сопоставления оси пространства имен XPath с моделью DOM или является ли это ошибкой.

+0

Я согласен, что это не проблема xPath 1.0 vs 2.0, но я пока не склонен думать, что проблема - это xpath API в Java 6 (хотя он может иметь различные недостатки) **, потому что ** когда я меняю из XML-парсера по умолчанию в Java 6 (Xalan) с Saxon 9 HE (при внесении ** никаких изменений ** в код Java) он работает (то есть он возвращает 16 узлов вместо 4). Это приводит меня к выводу, что реализация Xalan является реальной причиной. –

3

Как указано в предыдущем потоке, //namespace::* вернет все узлы пространства имен, из которых 16, в соответствии с реализациями XPath 1.0 и XPath 2.0. Меня это не удивляет, если вы нашли реализацию, которая не реализует спецификацию правильно.

Поиск всех объявлений пространства имен (в отличие от узлов пространства имен), как правило, не возможно ни с XPath 1.0, ни с XPath 2.0, так как следующие два документа считаются эквивалентными на уровне модели данных:

документ A:

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

документ B:

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

Но если мы определим "значительное объявление пространства имен" чтобы быть пространством имен, которое присутствует в дочернем элементе, но не на его родительском объекте, тогда вы можете попробовать это выражение XPath 2.0:

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

Хотя я очень хочу, чтобы этот ответ работал, так как он доходит до сути моей проблемы, этот xPath не возвращает ничего, отличное от того, которое я уже использую (// namespace :: *). Спасибо, однако, за попытку. –

Смежные вопросы