2009-09-14 30 views
8

Chúng tôi đang sử dụng dom4j 1.6.1, để phân tích cú pháp XML từ một nơi nào đó. Đôi khi, sự cân bằng đã đề cập đến không gian tên (ví dụ:) và đôi khi không(). Và nó làm cho cuộc gọi của Element.selectSingleNode (String s) thất bại.Xử lý không gian tên sạch với dom4j

Còn bây giờ chúng tôi có 3 giải pháp, và chúng tôi không hài lòng với họ

1 - Hủy bỏ tất cả các điều xảy ra không gian tên trước khi làm bất cứ điều gì với tài liệu xml

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

2 - Hủy bỏ namespace ngay trước khi nhận được một nút Bằng cách gọi

Element.remove(Namespace ns) 

Nhưng nó chỉ hoạt động cho một nút và mức độ đầu tiên của đứa trẻ

3 - rối rắm bây mã bằng cách

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

... Vì vậy, bạn nghĩ gì? Phù thủy một là tồi tệ hơn? Bạn có giải pháp khác để đề xuất?

Trả lời

1

Tùy chọn 1 là nguy hiểm vì bạn không thể đảm bảo tiền tố cho không gian tên đã cho mà không cần phân tích cú pháp tài liệu và vì bạn có thể kết thúc với xung đột không gian tên. Nếu bạn đang tiêu thụ một tài liệu và không xuất ra bất cứ điều gì, nó có thể là ok, tùy thuộc vào nguồn của tài liệu, nhưng nếu không nó chỉ mất quá nhiều thông tin.

Lựa chọn 2 có thể được áp dụng một cách đệ quy nhưng có nhiều vấn đề tương tự như tùy chọn của nó 1.

Lựa chọn 3 có vẻ như phương pháp tốt nhất, nhưng thay vì lộn xộn mã của bạn, tạo ra một phương pháp tĩnh mà cả hai kiểm tra chứ không phải hơn là đặt cùng một câu lệnh trong toàn bộ codebase của bạn.

Cách tiếp cận tốt nhất là để bất kỳ ai gửi cho bạn XML xấu để sửa lỗi đó. Tất nhiên điều này đặt ra câu hỏi là nó thực sự bị hỏng. Cụ thể, bạn có nhận được XML trong đó không gian tên mặc định được định nghĩa là X và sau đó một không gian tên cũng đại diện cho X được đưa ra một tiền tố của 'es'? Nếu đây là trường hợp thì XML được định dạng tốt và bạn chỉ cần mã không thuyết phục về tiền tố, nhưng vẫn sử dụng tên đủ điều kiện để tìm nạp phần tử. Tôi không quen thuộc với Dom4j để biết nếu tạo một Không gian tên với một tiền tố null sẽ khiến nó khớp với tất cả các phần tử với một URI phù hợp hay chỉ những phần tử không có tiền tố, nhưng nó có giá trị thử nghiệm.

+0

Tôi sẽ cố gắng đào tài liệu về không gian tên với tiền tố rỗng. Dù sao cũng cảm ơn bạn. Giới thiệu về nguồn của tệp XML: không phải là cách mà chúng thay đổi bất cứ điều gì. Nhưng tệp có hoặc không có không gian tên là hợp lệ. Với các tệp, chúng tôi xây dựng các đối tượng mà chúng tôi sử dụng trong hệ thống của mình. Nhưng chúng tôi không bao giờ "writte" một cái gì đó. (chúng tôi không có quyền sửa đổi tệp xml) –

4

Sau đây là một số mã mà tôi đã tìm thấy và hiện đang sử dụng. Có thể hữu ích, nếu tìm kiếm một cách chung chung, để loại bỏ tất cả các không gian tên từ một tài liệu 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); 
      }    
     } 
    } 

Hy vọng điều này hữu ích cho những người cần!

+0

không thể làm cho mã này hoạt động. Tôi đã sử dụng xml với không gian tên mẫu từ w3schools, nhưng nó giống như dom4j không nhận ra các không gian tên. Việc đầu tiên nếu (root.getNamespace()! = Không gian tên.NO_NAMESPACE) đánh giá là đúng, và ngay cả khi tôi loại bỏ nếu, nó vẫn không làm gì cả. – Dan

+0

Hi Dan, Điều này sẽ loại bỏ các không gian tên khỏi tài liệu. Có lẽ bạn đang quan tâm đến việc loại bỏ các tiền tố là tốt. – Abhishek

+0

Xin lỗi, do nhầm lẫn tôi đã lưu trước khi hoàn thành những gì tôi muốn viết! Dan, Hàm này loại bỏ các không gian tên khỏi tài liệu. Tôi đã thử điều này w/ví dụ thứ 5 từ w3schools. Bạn có thể xác minh điều này bằng cách tạo một xpath như "// table". Chạy xpath này trên tài liệu trước và sau khi gọi hàm removeNamespaces, và bạn sẽ thấy rằng hàm sau sẽ tìm thấy các nút cho bạn. Chính xác những gì bạn đang cố gắng để làm ? Tôi nghi ngờ nếu bạn quan tâm nhiều hơn đến việc chỉ xóa các tiền tố, ví dụ: (h: table -> table)? Hãy cho tôi biết nếu tôi có thể giúp được gì! – Abhishek

5

Tôi muốn xóa mọi thông tin không gian tên (khai báo và thẻ) để giảm bớt đánh giá xpath. Tôi kết thúc với giải pháp này:

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

nơi NameSpaceCleaner là một người truy cập 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() dường như không làm bất cứ điều gì, ít nhất là trong tài liệu của tôi, các cá thể không gian tên có các thuộc tính rỗng và tài liệu rỗng, ngăn không cho làm việc. Tôi đã phải sử dụng các yếu tố phụ huynh để thoát khỏi sự dư thừa lạ (tất cả các yếu tố có một tài sản QName đó là thực sự được sử dụng) Phần tử không gian con nút. Điều này là với dom4j-1.6.1. –

+0

Hoạt động hoàn hảo cho tôi! –

+0

Chú ý. Nếu bạn đi đến mã nguồn của reader.read(), bạn sẽ tìm thấy nó sẽ phân tích cú pháp nội dung xml với thiết lập nhận biết namesapce thành true (hardcoded dom4j 1.6). – artificerpi

0

Như Abhishek, tôi cần phải dải không gian tên từ XML để đơn giản hóa các truy vấn XPath trong các kịch bản thử nghiệm hệ thống.(XML được XSD đầu tiên xác nhận)

Dưới đây là những vấn đề tôi phải đối mặt:

  1. tôi cần thiết để xử lý XML có cấu trúc sâu sắc rằng đã có một xu hướng thổi lên stack.
  2. Trên hầu hết các XML phức tạp, vì lý do tôi không điều tra đầy đủ, việc loại bỏ tất cả các không gian tên chỉ hoạt động một cách đáng tin cậy khi đi qua chiều sâu cây DOM trước tiên. Vì vậy mà loại trừ những khách truy cập hoặc nhận được danh sách các nút với document.selectNodes("//*")

tôi đã kết thúc như sau (không phải là thanh lịch nhất, nhưng nếu điều đó có thể giúp giải quyết vấn đề của ai đó ...):

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

Mã này thực sự hoạt động:

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(); 
    } 
} 
Các vấn đề liên quan