Chuyển đổi phân đoạn chuỗi XML thành Nút tài liệu trong Java


77

Trong Java, làm thế nào bạn có thể chuyển đổi một Chuỗi đại diện cho một đoạn XML để chèn vào tài liệu XML?

ví dụ

String newNode =  "<node>value</node>"; // Convert this to XML

Sau đó, chèn nút này vào org.w3c.dom.Document làm nút con của một nút nhất định?


Câu trả lời:


65
Element node =  DocumentBuilderFactory
    .newInstance()
    .newDocumentBuilder()
    .parse(new ByteArrayInputStream("<node>value</node>".getBytes()))
    .getDocumentElement();

3
.parse (new StringInputStream (.... nên đọc .parse (new ByteArrayInputStream (new String ("xml") .getBytes ()));
Steen

5
Tôi chỉ ghét những commentboxes và họ thiếu đánh dấu (hoặc markdown, cho rằng vấn đề)
Steen

4
nhưng điều này không sao chép phần con ... ví dụ: nếu bạn làm điều này trong trường hợp "<tag1> <tag2> <tag3> blah </tag3> blah </tag2> </tag1> Nó chỉ nhận được <tag1> mà không có con của nó
grobartn

1
Điều này không hiệu quả với tôi vì nó không sao chép trẻ em như grobartn đã lưu ý. Giải pháp của @ McDowell đã hoạt động.
Nâng cấp

33

Bạn có thể sử dụng phương thức nhập (hoặc áp dụng ) của tài liệu để thêm các đoạn XML:

  /**
   * @param docBuilder
   *          the parser
   * @param parent
   *          node to add fragment to
   * @param fragment
   *          a well formed XML fragment
   */
  public static void appendXmlFragment(
      DocumentBuilder docBuilder, Node parent,
      String fragment) throws IOException, SAXException {
    Document doc = parent.getOwnerDocument();
    Node fragmentNode = docBuilder.parse(
        new InputSource(new StringReader(fragment)))
        .getDocumentElement();
    fragmentNode = doc.importNode(fragmentNode, true);
    parent.appendChild(fragmentNode);
  }

5
Hừ! Nếu đây là giải pháp đơn giản nhất, tôi phải nói rằng nó khá phức tạp cho một vấn đề nhỏ như vậy.
Jonik

Tôi đã giảm nó xuống mức tối thiểu - tuy nhiên, nó vẫn sử dụng những gì bạn nhận được trong API JRE, do đó, một chút dài dòng là không thể tránh khỏi.
McDowell

3
Đó chính xác là những gì tôi đang tìm kiếm. Tôi không nhận ra rằng tôi phải nhập phân đoạn vào dom trước khi gắn nó vào nút cha!
Tony Eichelberger

Nếu bạn không muốn chi tiết, bạn không được sử dụng Java, Luke. Cảm ơn vì câu trả lời, không có cơ hội cho bất kỳ ai hiểu ra điều đó.
Akku

Althoug câu trả lời đã chọn là chính xác với những gì người dùng yêu cầu, câu trả lời này đúng hơn.
flagsofnerd vào

15

Đối với những gì nó đáng giá, đây là một giải pháp tôi đã đưa ra bằng cách sử dụng thư viện dom4j . (Tôi đã kiểm tra xem nó có hoạt động không.)

Đọc phân đoạn XML thành một org.dom4j.Document(lưu ý: tất cả các lớp XML được sử dụng bên dưới là từ org.dom4j; xem Phụ lục):

  String newNode = "<node>value</node>"; // Convert this to XML
  SAXReader reader = new SAXReader();
  Document newNodeDocument = reader.read(new StringReader(newNode));

Sau đó lấy Tài liệu mà nút mới được chèn vào và Phần tử mẹ (sẽ là) từ đó. (Org.w3c.dom.Document của bạn sẽ cần được chuyển đổi thành org.dom4j.Document tại đây.) Với mục đích thử nghiệm, tôi đã tạo một tài liệu như thế này:

    Document originalDoc = 
      new SAXReader().read(new StringReader("<root><given></given></root>"));
    Element givenNode = originalDoc.getRootElement().element("given");

Thêm phần tử con mới rất đơn giản:

    givenNode.add(newNodeDocument.getRootElement());

Làm xong. Đầu ra originalDocbây giờ mang lại:

<?xml version="1.0" encoding="utf-8"?>

<root>
    <given>
        <node>value</node>
    </given>
</root>

Phụ lục : Vì câu hỏi của bạn nói về org.w3c.dom.Document, đây là cách chuyển đổi giữa câu hỏi đó và org.dom4j.Document.

// dom4j -> w3c
DOMWriter writer = new DOMWriter();
org.w3c.dom.Document w3cDoc = writer.write(dom4jDoc);

// w3c -> dom4j
DOMReader reader = new DOMReader();
Document dom4jDoc = reader.read(w3cDoc);

(Nếu bạn cần cả hai loại Documentthường xuyên, có thể hợp lý nếu đặt chúng trong các phương thức tiện ích gọn gàng, có thể trong một lớp được gọi XMLUtilshoặc một cái gì đó tương tự.)

Có thể có nhiều cách tốt hơn để làm điều này, ngay cả khi không có bất kỳ thư viện bên thứ ba nào. Nhưng trong số các giải pháp được trình bày cho đến nay, theo tôi đây là cách dễ nhất, ngay cả khi bạn cần thực hiện chuyển đổi dom4j <-> w3c.

Cập nhật (2011): trước khi thêm phụ thuộc dom4j vào mã của bạn, hãy lưu ý rằng không phải là một dự án được duy trì tích cực và cũng có một số vấn đề khác . Phiên bản cải tiến 2.0 đã có từ lâu nhưng chỉ có phiên bản alpha. Bạn có thể muốn xem xét một giải pháp thay thế, như XOM, để thay thế; đọc thêm trong câu hỏi được liên kết ở trên.


Nếu dom4j là KHÔNG ĐƯỢC ĐI, hãy thử giải pháp này: stackoverflow.com/a/7607435/363573
Stephan

6

Đây là một giải pháp khác, sử dụng thư viện XOM , cạnh tranh với câu trả lời dom4j của tôi . (Đây là một phần trong nhiệm vụ của tôi để tìm một sự thay thế dom4j tốt trong đó XOM được đề xuất như một tùy chọn.)

Trước tiên, hãy đọc đoạn XML thành nu.xom.Document:

String newNode = "<node>value</node>"; // Convert this to XML
Document newNodeDocument = new Builder().build(newNode, "");

Sau đó, lấy Tài liệu và Nút theo đó phân đoạn được thêm vào. Một lần nữa, vì mục đích thử nghiệm, tôi sẽ tạo Tài liệu từ một chuỗi:

Document originalDoc = new Builder().build("<root><given></given></root>", "");
Element givenNode = originalDoc.getRootElement().getFirstChildElement("given");

Bây giờ, việc thêm nút con rất đơn giản và tương tự như với dom4j (ngoại trừ việc XOM không cho phép bạn thêm phần tử gốc ban đầu đã thuộc về newNodeDocument):

givenNode.appendChild(newNodeDocument.getRootElement().copy());

Việc xuất tài liệu sẽ mang lại kết quả chính xác XML (và rất dễ dàng với XOM: chỉ cần in chuỗi được trả về originalDoc.toXML()):

<?xml version="1.0"?>
<root><given><node>value</node></given></root>

(Nếu bạn muốn định dạng XML độc đáo (với thụt lề và dòng), hãy sử dụng a Serializer; cảm ơn Peter Štibraný đã chỉ ra điều này.)

Vì vậy, phải thừa nhận rằng điều này không khác lắm so với giải pháp dom4j. :) Tuy nhiên, XOM có thể tốt hơn một chút để làm việc, vì API được ghi chép tốt hơn và vì triết lý thiết kế của nó mà có một cách kinh điển để thực hiện mỗi việc.

Phụ lục : Một lần nữa, đây là cách chuyển đổi giữa org.w3c.dom.Documentnu.xom.Document. Sử dụng các phương thức trợ giúp trong DOMConverterlớp của XOM :

// w3c -> xom
Document xomDoc = DOMConverter.convert(w3cDoc);

// xom -> w3c
org.w3c.dom.Document w3cDoc = DOMConverter.convert(xomDoc, domImplementation);  
// You can get a DOMImplementation instance e.g. from DOMImplementationRegistry

Lưu ý rằng thay vì mới Builder (). Build (new StringReader ("<root> <given> </given> </root>")); bạn cũng có thể sử dụng new Builder (). build ("<root> <given> </given> </root>", "test.xml"); (trong đó "test.xml" là một số URI cơ sở ngẫu nhiên)
Peter Štibraný

1
"Nếu bạn muốn định dạng XML độc đáo (với thụt lề và dòng cấp dữ liệu), tôi không chắc làm thế nào để làm điều đó với XOM." - sử dụng lớp Serializer. Định cấu hình nó bằng cách sử dụng setIndent và setMaxLength và gọi ghi (tài liệu).
Peter Štibraný

Serializer cũng dễ dàng tùy chỉnh bằng cách phân lớp con.
Peter Štibraný

Cảm ơn! Tôi không thực sự hiểu chính xác ý nghĩa của tham số baseURI là gì; truyền một chuỗi rỗng cũng hoạt động, vì vậy tôi đang sử dụng nó. Trong mọi trường hợp, điều đó đơn giản hóa mã phần nào. Đối với định dạng, Serializer thực sự hoạt động tốt.
Jonik

Tôi nghĩ baseURI sẽ được sử dụng để giải quyết các tài liệu tham khảo liên quan đến DTD hoặc XInclude ( lists.ibiblio.org/pipermail/xom-interest/2004-November/... )
Peter Štibraný

6
/**
*
* Convert a string to a Document Object
*
* @param xml The xml to convert
* @return A document Object
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static Document string2Document(String xml) throws IOException, SAXException, ParserConfigurationException {

    if (xml == null)
    return null;

    return inputStream2Document(new ByteArrayInputStream(xml.getBytes()));

}


/**
* Convert an inputStream to a Document Object
* @param inputStream The inputstream to convert
* @return a Document Object
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static Document inputStream2Document(InputStream inputStream) throws IOException, SAXException, ParserConfigurationException {
    DocumentBuilderFactory newInstance = DocumentBuilderFactory.newInstance();
    newInstance.setNamespaceAware(true);
    Document parse = newInstance.newDocumentBuilder().parse(inputStream);
    return parse;
}

4

Nếu bạn đang sử dụng dom4j, bạn chỉ có thể làm:

Tài liệu tài liệu = DocumentHelper.parseText (văn bản);

(dom4j hiện được tìm thấy tại đây: https://github.com/dom4j/dom4j )


Chỉ cần truy cập trang web của họ. Họ đặt Google Ads ngay vào thanh điều hướng điển hình do Maven tạo! Đáng kinh ngạc!
Thilo

2
Rõ ràng, các trang web không còn được điều hành bởi những kẻ dom4j, nhưng một số grabbers miền đã tiếp quản ...
Thilo

1

... và nếu bạn đang sử dụng hoàn toàn XOM, một cái gì đó như thế này:

    String xml = "<fakeRoot>" + xml + "</fakeRoot>";
    Document doc = new Builder( false ).build( xml, null );
    Nodes children = doc.getRootElement().removeChildren();
    for( int ix = 0; ix < children.size(); ix++ ) {
        otherDocumentElement.appendChild( children.get( ix ) );
    }

XOM sử dụng fakeRoot bên trong để làm khá nhiều điều tương tự, vì vậy nó sẽ an toàn, nếu không muốn nói là chính xác.


1

Hãy thử jcabi-xml , với một lớp lót:

Node node = new XMLDocument("<node>value</node>").node();

Lỗi bản dựng jcabi-xmlUnresolved references to [com.jcabi.xml] by class(es) on the Bundle-Classpath[Jar:dot]
Ikenna Anthony Oka vào
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.