Konvertieren Sie ein String-XML-Fragment in einen Dokumentknoten in Java

76

Wie können Sie in Java einen String konvertieren, der ein XML-Fragment zum Einfügen in ein XML-Dokument darstellt?

z.B

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

Fügen Sie diesen Knoten dann als untergeordnetes Element eines bestimmten Knotens in ein org.w3c.dom.Document ein.

bläulich
quelle

Antworten:

64
Element node =  DocumentBuilderFactory
    .newInstance()
    .newDocumentBuilder()
    .parse(new ByteArrayInputStream("<node>value</node>".getBytes()))
    .getDocumentElement();
izb
quelle
3
die .parse (neuer StringInputStream (.... sollte .parse lesen (neuer ByteArrayInputStream (neuer String ("xml") .getBytes ()));
Steen
5
Ich hasse diese Kommentarfelder und ihr fehlendes Markup (oder Markdown)
Steen
4
Dies kopiert jedoch nicht die untergeordneten Elemente. Wenn Sie dies beispielsweise im Fall von "<tag1> <tag2> <tag3> blah </ tag3> blah </ tag2> </ tag1> tun, wird <tag1> nur ohne ausgeführt." seine Kinder
grobartn
1
Das hat bei mir nicht funktioniert, weil es keine Kinder kopiert hat, wie von grobartn bemerkt. @ McDowell's Lösung hat funktioniert.
Upgradingdave
33

Sie können die Import- (oder Adoptions- ) Methode des Dokuments verwenden, um XML-Fragmente hinzuzufügen:

  /**
   * @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);
  }
McDowell
quelle
5
Hmm. Wenn dies die einfachste Lösung ist, muss ich sagen, dass es für ein so kleines Problem ziemlich kompliziert ist.
Jonik
Ich habe es auf das Minimum reduziert - es verwendet jedoch immer noch das, was Sie in der JRE-API erhalten, so dass ein bisschen Ausführlichkeit unvermeidlich ist.
McDowell
3
Genau das habe ich gesucht. Ich wusste nicht, dass ich das Fragment in den Dom importieren musste, bevor ich es an den übergeordneten Knoten anhängte!
Tony Eichelberger
Wenn Sie keine Ausführlichkeit wünschen, dürfen Sie kein Java verwenden, Luke. Vielen Dank für die Antwort, keine Chance für jemanden, das herauszufinden.
Akku
Obwohl die ausgewählte Antwort korrekt ist, wenn der Benutzer dies verlangt, ist diese Antwort "mehr" korrekt.
Schachofnerd
15

Hier ist eine Lösung, die ich mit dom4j gefunden habe Bibliothek gefunden habe. (Ich habe überprüft, ob es funktioniert.)

Lesen Sie das XML-Fragment in ein org.dom4j.Document(Hinweis: Alle unten verwendeten XML-Klassen stammen aus org.dom4j; siehe Anhang):

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

Holen Sie sich dann das Dokument, in das der neue Knoten eingefügt wird, und das übergeordnete Element (zu sein) daraus. (Ihr org.w3c.dom.Document müsste hier in org.dom4j.Document konvertiert werden.) Zu Testzwecken habe ich eines wie folgt erstellt:

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

Das Hinzufügen des neuen untergeordneten Elements ist sehr einfach:

    givenNode.add(newNodeDocument.getRootElement());

Erledigt. Die Ausgabe originalDocergibt jetzt:

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

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

Anhang : Da es sich bei Ihrer Frage um eine Frage handelt, erfahren Sie org.w3c.dom.Documenthier, wie Sie zwischen dieser und konvertieren 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);

(Wenn Sie beide Arten von Documents regelmäßig benötigen , ist es möglicherweise sinnvoll, diese in ordentliche Dienstprogrammmethoden zu integrieren, möglicherweise in eine Klasse namens XMLUtilsoder so ähnlich.)

Vielleicht gibt es bessere Möglichkeiten, dies auch ohne Bibliotheken von Drittanbietern zu tun. Von den bisher vorgestellten Lösungen ist dies meiner Ansicht nach der einfachste Weg, selbst wenn Sie die dom4j <-> w3c-Konvertierungen durchführen müssen.

Update (2011): Beachten Sie vor dem Hinzufügen der dom4j-Abhängigkeit zu Ihrem Code, dass es sich nicht um ein aktiv verwaltetes Projekt handelt und auch einige andere Probleme auftreten . Die verbesserte Version 2.0 ist seit Ewigkeiten in Arbeit, es ist jedoch nur eine Alpha-Version verfügbar. Möglicherweise möchten Sie stattdessen eine Alternative wie XOM in Betracht ziehen. Lesen Sie mehr in der oben verlinkten Frage.

Jonik
quelle
Wenn dom4j ein NO-GO ist, versuchen Sie diese Lösung: stackoverflow.com/a/7607435/363573
Stephan
6

Hier ist noch eine andere Lösung, die die XOM- Bibliothek verwendet und mit meiner dom4j-Antwort konkurriert . (Dies ist Teil meiner Suche nach einem guten dom4j-Ersatz, bei dem XOM als eine Option vorgeschlagen wurde.)

Lesen Sie zuerst das XML-Fragment in ein nu.xom.Document:

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

Holen Sie sich dann das Dokument und den Knoten, unter dem das Fragment hinzugefügt wird. Zu Testzwecken erstelle ich das Dokument erneut aus einer Zeichenfolge:

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

Das Hinzufügen des untergeordneten Knotens ist jetzt einfach und ähnlich wie bei dom4j (außer dass Sie mit XOM nicht das ursprüngliche Stammelement hinzufügen können, zu dem bereits gehört newNodeDocument):

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

Die Ausgabe des Dokuments liefert das richtige Ergebnis-XML (und ist mit XOM bemerkenswert einfach: Drucken Sie einfach die von zurückgegebene Zeichenfolge aus originalDoc.toXML()):

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

(Wenn Sie das XML gut formatieren möchten (mit Einrückungen und Zeilenvorschüben), verwenden Sie a Serializer; danke an Peter Štibraný für den Hinweis.)

Zugegeben, das unterscheidet sich nicht sehr von der dom4j-Lösung. :) Es ist jedoch möglicherweise etwas besser, mit XOM zu arbeiten, da die API besser dokumentiert ist und aufgrund ihrer Designphilosophie gibt es einen kanonischen Weg, um jede Sache zu erledigen.

Anhang : Hier erfahren Sie, wie Sie zwischen org.w3c.dom.Documentund konvertieren nu.xom.Document. Verwenden Sie die Hilfsmethoden in der XOM- DOMConverterKlasse:

// 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
Jonik
quelle
Beachten Sie, dass anstelle von new Builder (). Build (neuer StringReader ("<root> <given> </ given> </ root>")); Sie können auch new Builder () verwenden. build ("<root> <given> </ given> </ root>", "test.xml"); (wobei "test.xml" eine zufällige Basis-URI ist)
Peter Štibraný
1
"Wenn Sie das XML gut formatieren wollten (mit Einrückungen und Zeilenvorschüben), bin ich mir nicht sicher, wie ich das mit XOM machen soll." - Verwenden der Serializer-Klasse. Konfigurieren Sie es mit setIndent und setMaxLength und rufen Sie write (document) auf.
Peter Štibraný
Der Serializer kann auch einfach durch Unterklassen angepasst werden.
Peter Štibraný
Vielen Dank! Ich habe nicht wirklich verstanden, was genau die Bedeutung des baseURI-Parameters ist. Das Übergeben einer leeren Zeichenfolge funktioniert auch, also verwende ich das. In jedem Fall vereinfacht dies den Code etwas. Für die Formatierung funktioniert Serializer in der Tat einwandfrei.
Jonik
Ich denke, baseURI würde verwendet, um relative Verweise auf DTD oder XInclude ( lists.ibiblio.org/pipermail/xom-interest/2004-November/… ) aufzulösen
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;
}
Giordano Maestro
quelle
4

Wenn Sie dom4j verwenden, können Sie einfach Folgendes tun:

Dokument document = DocumentHelper.parseText (Text);

(dom4j jetzt hier zu finden: https://github.com/dom4j/dom4j )

Ronz
quelle
Ich bin gerade auf ihre Website gegangen. Sie platzieren Google Ads direkt in der typischen von Maven generierten Navigationsleiste! Unglaublich!
Thilo
2
Anscheinend wird die Seite nicht mehr von den dom4j-Leuten betrieben, aber einige Domain-Grabber übernahmen ...
Thilo
1

... und wenn Sie nur XOM verwenden, so etwas:

    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 verwendet fakeRoot intern, um fast dasselbe zu tun. Daher sollte es sicher, wenn nicht sogar elegant sein.

Atamar
quelle
1

Probieren Sie jcabi-xml mit einem Einzeiler aus :

Node node = new XMLDocument("<node>value</node>").node();
yegor256
quelle
jcabi-xml Build-FehlerUnresolved references to [com.jcabi.xml] by class(es) on the Bundle-Classpath[Jar:dot]
Ikenna Anthony Okafor