2010-10-15 13 views
14

Voglio manipolare il documento xml con spazio dei nomi predefinito ma senza prefisso. C'è un modo per usare xpath senza namespace uri come se non ci fosse spazio dei nomi?
Credo che dovrebbe essere possibile se impostiamo la proprietà namespaceAware di documentBuilderFactory su false. Ma nel mio caso non sta funzionando.
La mia comprensione non è corretta o sto facendo un errore nel codice?Come utilizzare XPath su documenti xml con spazio dei nomi predefinito

Ecco il mio codice:

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

Ecco il mio 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

Questo appare come lo stesso problema Default XML namespace, JDOM, and XPath belwood

risposta

21

L'elaborazione XPath per un documento che utilizza lo spazio dei nomi di default (senza prefisso) è lo stesso che l'elaborazione XPath per un documento che utilizza i prefissi:

Per i documenti qualificati dello spazio dei nomi è possibile utilizzare un NamespaceContext quando si esegue XPath. Dovrai anteporre i frammenti nell'XPath per far corrispondere il NamespaceContext. I prefissi che usi non devono necessariamente corrispondere ai prefissi usati nel documento.

Ecco come appare con il codice:

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

    } 

} 

Nota: ho usato anche la XPath corretta suggerito da Dennis.

Di seguito appare anche al lavoro, ed è più vicino alla tua domanda iniziale:

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 buona spiegazione per una FAQ. –

+0

Quindi dovrò passare allo scenario con nomi. Beh, una buona idea, ma sarò nell'inferno a farlo. Ho tonnellate di codice che gestisce attualmente xml senza namespace, usando xpath. Ho dovuto aggiungere lo spazio dei nomi predefinito per la convalida (da IDE e programmaticamente) scopo. C'è un modo per uccidere due piccioni con una fava?Voglio dire, potrei non dover modificare tutte le espressioni xpath e allo stesso tempo ottenere il documento validato sia in IDE che a livello di programmazione? – WSK

+0

Ho pensato di rimuovere lo spazio dei nomi. In tal caso non affronterò il problema di xpath e per la convalida programmatica potrei aggiungere namespace in fase di runtime. Forse, dovrò solo analizzare i miei documenti prima della convalida. Potrebbe essere accettabile, ma dopo averlo fatto, non vedo alcun modo per convalidare i miei documenti XML dall'IDE. Qualche altra idea? – WSK

1

Blaise Doughan è giusto, il codice allegato è corretto.
Il problema era in qualche luogo elese. Stavo eseguendo tutti i miei test tramite il launcher di applicazioni in Eclipse IDE e non funzionava nulla. Poi ho scoperto che il progetto Eclipse era causa di dolore. Ho eseguito la mia lezione dal prompt dei comandi, ha funzionato. Creato un nuovo progetto eclipse e incollato lo stesso codice lì, ha funzionato anche lì. Grazie a tutti ragazzi per il vostro tempo e impegno.

0

Ho scritto una semplice implementazione NamespaceContext (here), che potrebbe essere di aiuto. Ci vuole un Map<String, String> come input, dove lo key è un prefisso e lo value è uno spazio dei nomi.

Segue lo spesification NamespaceContext e potete vedere come funziona nello 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"] 

Nota che ha una dipendenza da Google Guava

Problemi correlati