2009-09-14 6 views
8

Stiamo usando dom4j 1.6.1, per analizzare la comunicazione XML da qualche parte. A volte, il balise ha menzionato lo spazio dei nomi (ad esempio:) e talvolta non(). Ed è la chiamata di Element.selectSingleNode (String s) non riesce.Pulizia della gestione spazio dei nomi con dom4j

Per ora abbiamo 3 soluzioni, e non siamo felici con loro

1 - Rimuovere tutti occorrenza spazio dei nomi prima di fare qualsiasi cosa con il documento XML

xml = xml .replaceAll("xmlns=\"[^\"]*\"",""); 
xml = xml .replaceAll("ds:",""); 
xml = xml .replaceAll("etm:",""); 
[...] // and so on for each kind of namespace 

2 - Rimuovere namespace poco prima di ottenere un nodo chiamando

Element.remove(Namespace ns) 

Ma è funziona solo per un nodo e il primo livello di bambino

3 - ingombrare il codice da

node = rootElement.selectSingleNode(NameWithoutNameSpace) 
if (node == null) 
    node = rootElement.selectSingleNode(NameWithNameSpace) 

Allora ... cosa ne pensi? La strega è la meno peggio? Hai un'altra soluzione da proporre?

risposta

1

L'opzione 1 è pericolosa perché non è possibile garantire i prefissi per un determinato spazio dei nomi senza eseguire il pre-analisi del documento e perché si può finire con la collisione nello spazio dei nomi. Se stai consumando un documento e non stai esportando nulla, potrebbe essere ok, a seconda della fonte del documento, ma altrimenti perde troppe informazioni.

opzione 2 potrebbe essere applicato in modo ricorsivo, ma la sua ha molti degli stessi problemi come opzione 1.

Opzione 3 suona come l'approccio migliore, ma piuttosto che il disordine il codice, fare un metodo statico che fa piuttosto entrambi i controlli di mettere la stessa dichiarazione if in tutto il codice base.

L'approccio migliore consiste nel chiedere a chiunque stia inviando il codice XML errato per risolverlo. Ovviamente questo pone la domanda è in realtà rotto. In particolare, stai ricevendo XML in cui lo spazio dei nomi predefinito è definito come X e quindi uno spazio dei nomi che rappresenta anche X ha un prefisso 'es'? Se questo è il caso, l'XML è ben formato e hai solo bisogno di codice che sia agnostico sul prefisso, ma usa comunque un nome qualificato per recuperare l'elemento. Non sono abbastanza familiare con Dom4j per sapere se creare un Namespace con un prefisso nullo farà in modo che corrisponda a tutti gli elementi con un URI corrispondente o solo a quelli senza prefisso, ma vale la pena sperimentarlo.

+0

cercherò di scavare il doc su namespace con il prefisso nullo. Grazie comunque. Informazioni sull'origine del file XML: theire non è il modo in cui cambiano nulla. Ma il file con o senza spazio dei nomi è valido. Con i file, costruiamo oggetti, che utilizziamo nel nostro sistema. Ma non abbiamo mai "scritto" qualcosa. (Non abbiamo il diritto di modificare il file xml) –

4

Di seguito è riportato un codice che ho trovato e ora uso. Potrebbe essere utile, se cerchi un modo generico, rimuovere tutti gli spazi dei nomi da un documento dom4j.

public static void removeAllNamespaces(Document doc) { 
     Element root = doc.getRootElement(); 
     if (root.getNamespace() != 
       Namespace.NO_NAMESPACE) {    
       removeNamespaces(root.content()); 
     } 
    } 

    public static void unfixNamespaces(Document doc, Namespace original) { 
     Element root = doc.getRootElement(); 
     if (original != null) { 
      setNamespaces(root.content(), original); 
     } 
    } 

    public static void setNamespace(Element elem, Namespace ns) { 

     elem.setQName(QName.get(elem.getName(), ns, 
       elem.getQualifiedName())); 
    } 

    /** 
    *Recursively removes the namespace of the element and all its 
    children: sets to Namespace.NO_NAMESPACE 
    */ 
    public static void removeNamespaces(Element elem) { 
     setNamespaces(elem, Namespace.NO_NAMESPACE); 
    } 

    /** 
    *Recursively removes the namespace of the list and all its 
    children: sets to Namespace.NO_NAMESPACE 
    */ 
    public static void removeNamespaces(List l) { 
     setNamespaces(l, Namespace.NO_NAMESPACE); 
    } 

    /** 
    *Recursively sets the namespace of the element and all its children. 
    */ 
    public static void setNamespaces(Element elem, Namespace ns) { 
     setNamespace(elem, ns); 
     setNamespaces(elem.content(), ns); 
    } 

    /** 
    *Recursively sets the namespace of the List and all children if the 
    current namespace is match 
    */ 
    public static void setNamespaces(List l, Namespace ns) { 
     Node n = null; 
     for (int i = 0; i < l.size(); i++) { 
      n = (Node) l.get(i); 

      if (n.getNodeType() == Node.ATTRIBUTE_NODE) { 
       ((Attribute) n).setNamespace(ns); 
      } 
      if (n.getNodeType() == Node.ELEMENT_NODE) { 
       setNamespaces((Element) n, ns); 
      }    
     } 
    } 

Spero che questo sia utile per qualcuno che ne ha bisogno!

+0

non può far funzionare questo codice. Ho usato xml con esempi di spazi dei nomi da w3schools, ma è come se dom4j non riconoscesse gli spazi dei nomi. Il primo if (root.getNamespace()! = Namespace.NO_NAMESPACE) restituisce true, e anche se rimuovo il if, non fa ancora nulla. – Dan

+0

Ciao Dan, questo rimuove gli spazi dei nomi dal documento. Probabilmente ti interessa anche rimuovere i prefissi. – Abhishek

+0

Siamo spiacenti, per errore ho salvato prima di completare ciò che volevo scrivere! Dan, questa funzione rimuove gli spazi dei nomi dal documento. Ho provato questo w/il 5 ° esempio da w3schools. Puoi verificarlo creando un xpath come "// table". Esegui xpath sul documento prima e dopo aver chiamato la funzione removeNamespaces e vedrai che quest'ultimo troverà i nodi per te. Cosa stai cercando di fare esattamente ? Dubito che tu sia più interessato a rimuovere solo i prefissi, per esempio (h: tabella -> tabella)? Fammi sapere se posso essere di qualche aiuto! – Abhishek

5

Desidero rimuovere qualsiasi informazione sullo spazio dei nomi (dichiarazione e tag) per facilitare la valutazione xpath. Io alla fine con questa soluzione:

String xml = ... 
SAXReader reader = new SAXReader(); 
Document document = reader.read(new ByteArrayInputStream(xml.getBytes())); 
document.accept(new NameSpaceCleaner()); 
return document.asXML(); 

dove il NameSpaceCleaner è un visitatore dom4j:

private static final class NameSpaceCleaner extends VisitorSupport { 
    public void visit(Document document) { 
     ((DefaultElement) document.getRootElement()) 
       .setNamespace(Namespace.NO_NAMESPACE); 
     document.getRootElement().additionalNamespaces().clear(); 
    } 
    public void visit(Namespace namespace) { 
     namespace.detach(); 
    } 
    public void visit(Attribute node) { 
     if (node.toString().contains("xmlns") 
     || node.toString().contains("xsi:")) { 
     node.detach(); 
     } 
    } 

    public void visit(Element node) { 
     if (node instanceof DefaultElement) { 
     ((DefaultElement) node).setNamespace(Namespace.NO_NAMESPACE); 
     } 
     } 
} 
+0

Namespace.detach() non sembra fare nulla, almeno nel mio documento le istanze del namespace hanno avuto genitori nulli e proprietà del documento null, impedendo il distacco dal lavoro. Ho dovuto usare l'elemento genitore per sbarazzarmi dello strano ridondante (tutti gli elementi hanno una proprietà QName che viene effettivamente utilizzata) Nodi figlio degli elementi dello spazio dei nomi. Questo era con dom4j-1.6.1. –

+0

Funziona perfettamente per me! –

+0

Attenzione. Se vai al codice sorgente di reader.read(), troverai che analizzerà il contenuto xml con l'impostazione namesapce aware a true (hardcoded dom4j 1.6). – artificerpi

0

Come Abhishek, avevo bisogno di togliere lo spazio dei nomi da XML per semplificare la query XPath negli script di test del sistema.(XML viene prima XSD convalidato)

Qui ci sono i problemi che ho riscontrato:

  1. avevo bisogno per elaborare XML profondamente strutturato che aveva la tendenza di far saltare in pila.
  2. Nella maggior parte degli XML complessi, per una ragione non ho investigato completamente, eliminando tutti gli spazi dei nomi funzionati in modo affidabile quando si attraversava prima la profondità dell'albero DOM. Così che escludeva il visitatore, o di ottenere l'elenco dei nodi con document.selectNodes("//*")

ho finito con il seguente (non il più elegante, ma se questo può aiutare a risolvere il problema di qualcuno ...):

public static String normaliseXml(final String message) { 
    org.dom4j.Document document; 
    document = DocumentHelper.parseText(message); 

    Queue stack = new LinkedList(); 

    Object current = document.getRootElement(); 

    while (current != null) { 
     if (current instanceof Element) { 
      Element element = (Element) current; 

      Iterator iterator = element.elementIterator(); 

      if (iterator.hasNext()) { 
       stack.offer(element); 
       current = iterator; 
      } else { 
       stripNamespace(element); 

       current = stack.poll(); 
      } 
     } else { 
      Iterator iterator = (Iterator) current; 

      if (iterator.hasNext()) { 
       stack.offer(iterator); 
       current = iterator.next(); 
      } else { 
       current = stack.poll(); 

       if (current instanceof Element) { 
        stripNamespace((Element) current); 

        current = stack.poll(); 
       } 
      } 
     } 
    } 

    return document.asXML(); 
} 

private static void stripNamespace(Element element) { 
    QName name = new QName(element.getName(), Namespace.NO_NAMESPACE, element.getName()); 
    element.setQName(name); 

    for (Object o : element.attributes()) { 
     Attribute attribute = (Attribute) o; 

     QName attributeName = new QName(attribute.getName(), Namespace.NO_NAMESPACE, attribute.getName()); 
     String attributeValue = attribute.getValue(); 

     element.remove(attribute); 

     element.addAttribute(attributeName, attributeValue); 
    } 

    for (Object o : element.declaredNamespaces()) { 
     Namespace namespace = (Namespace) o; 
     element.remove(namespace); 
    } 
} 
0

Questo codice funziona davvero:

public void visit(Document document) { 
    ((DefaultElement) document.getRootElement()) 
      .setNamespace(Namespace.NO_NAMESPACE); 
    document.getRootElement().additionalNamespaces().clear(); 
} 

public void visit(Namespace namespace) { 
    if (namespace.getParent() != null) { 
     namespace.getParent().remove(namespace); 
    } 
} 

public void visit(Attribute node) { 
    if (node.toString().contains("xmlns") 
      || node.toString().contains("xsi:")) { 
     node.getParent().remove(node); 
    } 
} 

public void visit(Element node) { 
    if (node instanceof DefaultElement) { 
     ((DefaultElement) node).setNamespace(Namespace.NO_NAMESPACE); 
     node.additionalNamespaces().clear(); 
    } 
}