2009-11-13 16 views
12

C'è un modo per impostare XPath di Java per avere un prefisso dello spazio dei nomi predefinito per gli Expresson? Ad esempio, invece di:/html: html/html: head/html: title/text() ", la query potrebbe essere:/html/head/title/text()XPath: esiste un modo per impostare uno spazio dei nomi predefinito per le query?

Mentre si utilizza il prefisso dei nomi, funziona, ci deve essere un modo più elegante codice

esempio di snippet di quello che sto facendo ora:.

Node node = ... // DOM of a HTML document 
XPath xpath = XPathFactory.newInstance().newXPath(); 

// set to a NamespaceContext that simply returns the prefix "html" 
// and namespace URI ""http://www.w3.org/1999/xhtml" 
xpath.setNamespaceContext(new HTMLNameSpace()); 

String expression = "/html:html/html:head/html:title/text()"; 
String value = xpath.evaluate(query, expression); 

risposta

10

Purtroppo, no. Si è parlato della definizione di uno spazio dei nomi predefinito per JxPath alcuni anni fa, ma una rapida occhiata agli ultimi documenti non indica che sia successo qualcosa. Potresti volere ancora un po 'di tempo a guardare attraverso i documenti.

Una cosa che potresti fare, se davvero non ti interessa degli spazi dei nomi, è analizzare il documento senza di loro. Devi semplicemente omettere la chiamata che stai attualmente facendo a DocumentBuilderFactory.setNamespaceAware().

Inoltre, si noti che il prefisso può essere qualsiasi cosa si desideri; non deve corrispondere al prefisso nel documento di istanza. Quindi è possibile utilizzare h anziché html e ridurre al minimo l'ingombro visivo del prefisso.

+0

Grazie per i suggerimenti. Ho finito per disattivare la consapevolezza dello spazio dei nomi poiché non era necessario per questo semplice caso (cioè lavorando sempre solo con uno spazio dei nomi). – Rob

+0

Come parte, XPath2.0 consente i caratteri jolly dello spazio dei nomi, quindi '*: html/*: head' ad esempio corrisponderà a una testa all'interno di un html in qualsiasi spazio dei nomi. – biziclop

+0

Come altro, JAXP 1.6 (utilizzato in Java 8) è ancora bloccato con XPath 1.0 (https://jaxp.java.net/docs/spec/pdf/JAXP1_6-FinalSpec.pdf 3.5) – Abdull

4

non ho effettivamente provato questo, ma in base alla documentazione NamespaceContext, il contesto spazio dei nomi con il prefisso "" (emtpy string) è considerato lo spazio dei nomi predefinito


Ero un po 'troppo veloce su quello. L'analizzatore XPath non invoca NamespaceContext per risolvere il prefisso "", se non viene utilizzato alcun prefisso nell'espressione XPath "/ html/head/title/text()". Vado ora ai dettagli XML, di cui non sono sicuro al 100%, ma l'utilizzo di un'espressione come "/: html /: head /: title/text()" funziona con Sun JDK 1.6.0_16 e viene richiesto NamespaceContext per risolvere un prefisso vuoto (""). Questo comportamento veramente corretto e previsto o un bug in Xalan?

+1

Per XPath 1.0 spec (http://www.w3.org/TR/1999/REC-xpath-19991116#node-tests), un test di nodo può utilizzare un "QName", che è definito dalla specifica Namespace (http://www.w3.org/TR/REC-xml-names/#NT-QName). Il prefisso di un QName è un NCName, che deve iniziare con una lettera o underscore (http://www.w3.org/TR/REC-xml-names/#NT-NCName). Tutto ciò equivale a dire che il valutatore JDK è rotto - anche se, in pratica, è improbabile che venga risolto. – kdgregory

2

So che questa domanda è vecchia ma ho appena trascorso 3 ore a cercare di risolvere questo problema e @kdgregorys answer mi ha aiutato molto. Volevo solo mettere esattamente quello che ho fatto usando kdgregorys come guida.

Il problema è che XPath in java non cerca nemmeno uno spazio dei nomi se non si dispone di un prefisso sulla query, quindi per mappare una query in uno spazio dei nomi specifico è necessario aggiungere un prefisso alla query. Ho usato un prefisso arbitrario per mappare il nome dello schema. Per questo esempio userò lo spazio dei nomi e la query di OP e il prefisso abc. La vostra nuova espressione sarebbe simile a questa:

String expression = "/abc:html/abc:head/abc:title/text()";

quindi effettuare le seguenti

1) accertarsi che il documento è impostato su namespace consapevoli.

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
factory.setNamespaceAware(true); 

2) implementare un NamespaceContext che risolverà il prefisso. Questo ho preso da qualche altro post su SO e modificato un po '

.

public class NamespaceResolver implements NamespaceContext { 

    private final Document document; 

    public NamespaceResolver(Document document) { 
     this.document = document; 
    } 

    public String getNamespaceURI(String prefix) { 
     if(prefix.equals("abc")) { 
      // here is where you set your namespace 
      return "http://www.w3.org/1999/xhtml"; 
     } else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) { 
      return document.lookupNamespaceURI(null); 
     } else { 
      return document.lookupNamespaceURI(prefix); 
     } 
    } 

    public String getPrefix(String namespaceURI) { 
     return document.lookupPrefix(namespaceURI); 
    } 

    @SuppressWarnings("rawtypes") 
    public Iterator getPrefixes(String namespaceURI) { 
     // not implemented 
     return null; 
    } 

} 

3) Quando si crea l'oggetto XPath impostare il vostro NamespaceContext.

xPath.setNamespaceContext(new NamespaceResolver(document)); 

Ora non importa quale sia il prefisso dello schema attuale è che si può utilizzare il proprio prefisso che mapperà al corretto schema. Quindi il tuo codice completo utilizzando la classe sopra sarebbe simile a questo.

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
factory.setNamespaceAware(true); 

Document document = factory.newDocumentBuilder().parse(sourceDocFile); 

XPathFactory xPFactory = XPathFactory.newInstance(); 
XPath xPath = xPFactory.newXPath(); 
xPath.setNamespaceContext(new NamespaceResolver(document)); 

String expression = "/abc:html/abc:head/abc:title/text()"; 
String value = xpath.evaluate(query, expression); 
Problemi correlati