2009-09-14 13 views
8

XML gelenleri bir yerden ayrıştırmak için dom4j 1.6.1 kullanıyoruz. Bazen, balerin ad alanından (örneğin:) bahsetmiştir ve bazen de() değildir. Ve Element.selectSingleNode (String s) çağrısı başarısız olur.Temiz alan kullanımı ile dom4j'yi temizle

Şimdilik biz 3 çözümler var ve biz onlara

1 ile mutlu değil - sadece bir almadan önce ad çıkarın - xml belgesinde

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

2 ile bir şey yapmadan önce tüm ad alanı oluşumunu kaldır

Element.remove(Namespace ns) 

arayarak düğüm Ama bu bir düğüm ve çocuğun

01 ilk seviye için çalışır oluyor

3 - Yani

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

tarafından kod dağınıklığı ... ne düşünüyorsun? Cadıdan biri daha mı kötü? Teklif etmek için başka bir çözüm var mı?

cevap

1

Seçenek 1, belgeyi önceden ayrıştırmadan belirli bir ad alanı için önekleri garanti edemediğinizden ve ad alanı çakışmasıyla sonuçlanabildiğinizden dolayı tehlikelidir. Bir belgeyi tüketiyorsanız ve hiçbir şey üretmiyorsanız, dokümanın kaynağına bağlı olarak sorun olmayabilir, ancak aksi halde çok fazla bilgi kaybeder.

Seçenek 2 yinelemeli uygulanabilir ama onun seçeneğiyle aynı sorunların çoğunu var 1.

Seçenek 3 en iyi yaklaşım gibi geliyor, ama yerine dağınıklığı daha kodunuzu, her iki tarafı yerine kontrolleri yapan bir statik yöntemi yapmak codebase'inizde aynı ifadeyi koymaktan daha iyi.

En iyi yaklaşım, düzeltmek için size kötü XML gönderen kişiyi almaktır. Tabii ki bu soru gerçekten kırılıyor. Özellikle, varsayılan ad alanının X olarak tanımlandığı XML'i alıyorsunuz ve daha sonra X'i temsil eden bir ad alanına 'es' öneki verilir mi? Bu durumda, XML iyi biçimlendirilmiş ve sadece önek hakkında agnostik bir kod gerekiyor, ancak hala öğeyi almak için nitelikli bir ad kullanır. Boş bir önek içeren bir Namespace oluşturmanın, eşleşen bir URI ile tüm öğeleri eşleştirmesine veya yalnızca öneki olmayanlara, ancak denemeye değer bir değere sahip olup olmadığını bilmek Dom4j ile yeterince tanıdık değilim.

+0

denemek ve boş öneki ile ad hakkında doc kazmak olacak. Yine de teşekkürler. XML dosyasının kaynağı hakkında: herhangi bir şeyi değiştirmenin yolu yoktur. Ancak isim alanı olan veya olmayan dosya geçerlidir. Dosyalarla, sistemimizde kullandığımız nesneleri oluştururuz. Ama asla bir şey "yazmıyoruz". (xml dosyasını değiştirme hakkımız yok) –

4

Aşağıda, bulmuş olduğum ve şimdi kullandığım bazı kodlar yer almaktadır. Genel bir yol arıyorsanız dom4j belgesindeki tüm ad alanlarını kaldırmak yararlı olabilir.

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

Bu, ihtiyacı olan biri için yararlıdır!

+0

bu kodu işe yaradıramadı. W3schools ad örnekleriyle xml kullandım, ama dom4j ad alanlarını tanımıyor gibi. Birincisi (root.getNamespace()! = Ad alanı. NO_NAMESPACE) true olarak değerlendirir ve eğer hala kaldırırsam bile yine de hiçbir şey yapmaz. – Dan

+0

Merhaba Dan, Bu, ad alanlarını belgeden kaldırır. Muhtemelen önekleri de kaldırmakla ilgileniyorsunuz. – Abhishek

+0

Özür dilerim, yanlışlıkla yazmayı istediğim şeyi tamamlamadan önce kaydettim! Dan, Bu işlev ad alanlarını belgeden kaldırır. Bu 5/5 örneğini w3schools denedim. Bunu, "// table" gibi bir xpath oluşturarak doğrulayabilirsiniz. RemoveNamespaces işlevini çağırmadan önce ve sonra belgede bu xpath'i çalıştırın ve sonuncusunun sizin için düğümleri bulacağını göreceksiniz. Tam olarak ne yapmaya çalışıyorsun? Öneklerin kaldırılmasıyla daha çok ilgileniyorsanız, örneğin (h: tablo -> tablo) ile ilgili şüpheniz var mı? Yardım edebilecek olursam haber ver! – Abhishek

5

xpath değerlendirmesini kolaylaştırmak için herhangi bir ad alanı bilgisini (bildirim ve etiket) kaldırmak istedim. Bu çözüm ile sona: NameSpaceCleaner bir dom4j ziyaretçimizin

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

: Abhishek olarak

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() herhangi bir şey yapmıyor gibi görünüyor, en azından Belgelerimdeki Ad Alanı örneklerinde boştaki ebeveynler ve boş belge özellikleri vardı, çalışma kopmasını engelliyordu. Garip yedekten kurtulmak için ana öğeyi kullanmalıydım (tüm elemanların aslında kullanılan bir QName özelliği vardır) Öğe Ad alanı çocuk düğümleri. Bu dom4j-1.6.1 ile yapıldı. –

+0

Benim için mükemmel çalışıyor! –

+0

Dikkat. Eğer reader.read() 'ın kaynak koduna giderseniz, xml içeriğini isim-alanı farkında ayarı ile doğru olarak (parlatılmış dom4j 1.6) ayrıştırırsınız. – artificerpi

0

, ben sistem test komut XPath sorguları basitleştirmek için XML ad şerit gerekiyordu.(XML birinci XSD doğrulandığında)

Burada karşılaşılan sorunlardır:

  1. I yığın şişirme eğilimi derin yapılandırılmış XML işlemek için gerekli.
  2. Çoğu karmaşık XML'de, tam olarak araştırmamam nedeniyle, tüm ad alanlarını sıyırma işlemi yalnızca DOM ağacı derinliğini ilk kez geçerken güvenilir bir şekilde çalıştı. Yani document.selectNodes("//*")

ile düğümlerin listesini alma ziyaretçinin, ya da dışarıda ben şu ile sona erdi (en zarif değil, ama bu yardımcı olabilir birilerinin sorununu ... çözme):

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

Bu kod gerçekten çalışıyor:

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