2010-10-15 13 views
14

Chcę manipulować dokumentem xml mającym domyślny obszar nazw, ale bez prefiksu. Czy istnieje sposób użycia xpath bez przestrzeni nazw uri tak, jakby nie było przestrzeni nazw?
Wierzę, że powinno być możliwe, jeśli ustawimy właściwość namesPaceAware właściwości documentBuilderFactory na wartość false. Ale w moim przypadku to nie działa.
Czy moje zrozumienie jest nieprawidłowe lub robię jakiś błąd w kodzie?Jak używać XPath na dokumentach xml z domyślną przestrzenią nazw

Oto mój kod:

DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); 
    domFactory.setNamespaceAware(false); 
    try { 
     DocumentBuilder builder = domFactory.newDocumentBuilder(); 
     Document dDoc = builder.parse("E:/test.xml"); 

     XPath xPath = XPathFactory.newInstance().newXPath(); 
     NodeList nl = (NodeList) xPath.evaluate("//author", dDoc, XPathConstants.NODESET); 
     System.out.println(nl.getLength()); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 

Oto mój xml:

<?xml version="1.0" encoding="UTF-8"?> 
<root xmlns="http://www.mydomain.com/schema"> 
    <author> 
    <book title="t1"/> 
    <book title="t2"/> 
    </author> 
</root> 
+0

to wygląda na ten sam problem Default XML namespace, JDOM, and XPath belwood

Odpowiedz

21

Przetwarzanie XPath dla danego dokumentu, który korzysta z domyślnej przestrzeni nazw (bez prefiksu) jest taka sama jak przetwarzania XPath dla dokumentu, który używa prefiksów:

Dla dokumentów kwalifikowanych do przestrzeni nazw można użyć Przestrzeni przestrzeni nazw po uruchomieniu XPath. Będziesz musiał poprzedzić fragmenty w XPath, aby dopasować NamespaceContext. Używane prefiksy nie muszą odpowiadać prefiksom użytym w dokumencie.

Oto jak to wygląda z kodem:

import java.util.Iterator; 
import javax.xml.namespace.NamespaceContext; 
import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.xpath.XPath; 
import javax.xml.xpath.XPathConstants; 
import javax.xml.xpath.XPathFactory; 
import org.w3c.dom.Document; 
import org.w3c.dom.NodeList; 

public class Demo { 

    public static void main(String[] args) { 
     DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); 
     domFactory.setNamespaceAware(true); 
     try { 
      DocumentBuilder builder = domFactory.newDocumentBuilder(); 
      Document dDoc = builder.parse("E:/test.xml"); 

      XPath xPath = XPathFactory.newInstance().newXPath(); 
      xPath.setNamespaceContext(new MyNamespaceContext()); 
      NodeList nl = (NodeList) xPath.evaluate("/ns:root/ns:author", dDoc, XPathConstants.NODESET); 
      System.out.println(nl.getLength()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    private static class MyNamespaceContext implements NamespaceContext { 

     public String getNamespaceURI(String prefix) { 
      if("ns".equals(prefix)) { 
       return "http://www.mydomain.com/schema"; 
      } 
      return null; 
     } 

     public String getPrefix(String namespaceURI) { 
      return null; 
     } 

     public Iterator getPrefixes(String namespaceURI) { 
      return null; 
     } 

    } 

} 

Uwaga: użyłem również skorygowany XPath sugerowanego przez Dennis.

Poniższa pojawia się również do pracy, i jest bliżej do pierwotnego pytania:

import javax.xml.parsers.DocumentBuilder; 
import javax.xml.parsers.DocumentBuilderFactory; 
import javax.xml.xpath.XPath; 
import javax.xml.xpath.XPathConstants; 
import javax.xml.xpath.XPathFactory; 

import org.w3c.dom.Document; 
import org.w3c.dom.NodeList; 

public class Demo { 

    public static void main(String[] args) { 
     DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); 
     try { 
      DocumentBuilder builder = domFactory.newDocumentBuilder(); 
      Document dDoc = builder.parse("E:/test.xml"); 

      XPath xPath = XPathFactory.newInstance().newXPath(); 
      NodeList nl = (NodeList) xPath.evaluate("/root/author", dDoc, XPathConstants.NODESET); 
      System.out.println(nl.getLength()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

} 
+1

+1 dobre wytłumaczenie dla FAQ. –

+0

Więc będę musiał przejść do scenariusza z przypisanymi nazwami. Cóż, to dobry pomysł, ale będę w piekle. Mam mnóstwo kodu, który aktualnie obsługuje xml bez przestrzeni nazw, za pomocą xpath. Musiałem dodać domyślny obszar nazw do celów walidacji (według IDE i programowo). Czy istnieje sposób na zabicie dwóch ptaków jednym kamieniem?Chodzi mi o to, że nie muszę edytować wszystkich wyrażeń xpath, a jednocześnie mogę sprawdzić poprawność dokumentu zarówno w IDE, jak i programowo? – WSK

+0

Myślałem, aby usunąć przestrzeń nazw. W takim przypadku nie napotkam problemu z xpath i dla walidacji programowej mogę dodać przestrzeń nazw w czasie wykonywania. Być może będę musiał przeanalizować tylko moje dokumenty przed zatwierdzeniem. Może to być dopuszczalne, ale po wykonaniu tego, nie widzę żadnego sposobu sprawdzania poprawności moich dokumentów XML przez IDE. Każdy inny pomysł? – WSK

1

Blaise Doughan ma rację, dołączony kod jest poprawny.
Problem był gdzieś indziej. Wszystkie moje testy przeprowadziłem za pomocą programu uruchamiającego aplikacje w środowisku Eclipse IDE i nic nie działało. Potem odkryłem, że projekt Eclipse był przyczyną wszelkiego smutku. Uruchomiłem swoją klasę z wiersza poleceń, zadziałało. Utworzono nowy projekt zaćmienia i wkleiłem tam ten sam kod, który również działał. Dziękuję wszystkim za poświęcony czas i wysiłek.

0

Napisałem prostą implementację NamespaceContext (here), która może być pomocna. Jako wejście przyjmuje się Map<String, String>, gdzie key jest prefiksem, a value jest przestrzenią nazw.

Podąża za statystykami NamespaceContext i można zobaczyć, jak to działa w unit tests.

Map<String, String> mappings = new HashMap<>(); 
mappings.put("foo", "http://foo"); 
mappings.put("foo2", "http://foo"); 
mappings.put("bar", "http://bar"); 

context = new SimpleNamespaceContext(mappings); 

context.getNamespaceURI("foo"); // "http://foo" 
context.getPrefix("http://foo"); // "foo" or "foo2" 
context.getPrefixes("http://foo"); // ["foo", "foo2"] 

Należy pamiętać, że ma ona zależność Google Guava

Powiązane problemy