Перетворити фрагмент рядка XML у вузол документа на Java


77

Як на Java можна перетворити рядок, який представляє фрагмент XML, для вставки в документ XML?

напр

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

Потім вставити цей вузол в org.w3c.dom.Document як дочірній матеріал даного вузла?


Дивіться також: stackoverflow.com/a/7607435/363573
Стефан

Відповіді:


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

3
.parse (new StringInputStream (.... повинен читати .parse (new ByteArrayInputStream (new String ("xml") .getBytes ()));
Стін

5
Я просто ненавиджу ці скриньки коментарів та їх відсутність розмітки (або, натомість, націнки)
Стін,

4
але це не копіює дітей ... наприклад, якщо ви зробите це у випадку "<tag1> <tag2> <tag3> blah </tag3> blah </tag2> </tag1> Це отримує лише <tag1> без його діти
гробартн

1
Це не спрацювало для мене, оскільки не копіювало дітей, як зазначив Гробартн. Рішення @ McDowell справді спрацювало.
Upgradingdave

33

Ви можете використовувати метод імпорту (або прийняття ) документа, щоб додати фрагменти 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
Хм Якщо це найпростіше рішення, я повинен сказати, що воно досить складне для такої невеликої проблеми.
Джонік

Я звів його до мінімуму - він все одно використовує те, що ви отримуєте в JRE API, однак, трохи деталізації не уникнути.
McDowell

3
Це саме те, що я шукав. Я не розумів, що повинен імпортувати фрагмент у dom, перш ніж додавати його до батьківського вузла!
Tony Eichelberger

Якщо багатослів'я ви не хочете, ви не повинні використовувати Java, Лука. Дякую за відповідь, ніхто не має шансів це зрозуміти.
Акку

Незважаючи на те, що вибрана відповідь є правильною, враховуючи те, що запитав користувач, цей ансер є "більш" правильним.
chessofnerd

15

Для чого це варте, ось рішення, яке я придумав, використовуючи бібліотеку dom4j . (Я перевірив, що це працює.)

Прочитайте фрагмент XML у org.dom4j.Document(примітка: усі використовувані нижче класи XML походять з org.dom4j; див. Додаток):

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

Потім отримайте Документ, до якого вставлений новий вузол, і батьківський Елемент (який має бути) з нього. (Ваш org.w3c.dom.Document потрібно буде тут перетворити на org.dom4j.Document.) Для цілей тестування я створив такий:

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

Додавати новий дочірній елемент дуже просто:

    givenNode.add(newNodeDocument.getRootElement());

Готово. Виведення originalDocтепер дає:

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

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

Додаток : Оскільки у вашому запитанні йдеться про org.w3c.dom.Document, ось як перетворити між цим і 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);

(Якщо вам Documentрегулярно потрібні обидва типи , можливо, має сенс помістити їх у акуратні утилітні методи, можливо, у клас, який називається, XMLUtilsабо щось подібне.)

Можливо, є кращі способи зробити це, навіть не маючи сторонніх бібліотек. Але з представлених на сьогодні рішень, на мій погляд, це найпростіший спосіб, навіть якщо вам потрібно виконати перетворення dom4j <-> w3c.

Оновлення (2011): перед додаванням до коду залежності dom4j зверніть увагу, що це не проект, який активно підтримується, і у нього також є деякі інші проблеми . Вдосконалена версія 2.0 працює протягом багатьох століть, але доступна лише альфа-версія. Ви можете замість цього розглянути альтернативу, наприклад XOM; детальніше читайте у зв’язаному вище запитанні.


Якщо dom4j NO-GO, спробуйте таке рішення: stackoverflow.com/a/7607435/363573
Стефан

6

Ось ще одне рішення, яке використовує бібліотеку XOM , яке конкурує з моєю відповіддю dom4j . (Це частина мого прагнення знайти хорошу заміну dom4j, де XOM був запропонований як один із варіантів.)

Спочатку прочитайте фрагмент XML у nu.xom.Document:

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

Потім отримайте документ і вузол, під який додано фрагмент. Знову ж таки, для цілей тестування я буду створювати документ із рядка:

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

Тепер додавання дочірнього вузла є простим і подібним до dom4j (за винятком того, що XOM не дозволяє додавати початковий кореневий елемент, якому вже належить newNodeDocument):

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

Виведення документа дає правильний результат XML (і це надзвичайно просто з XOM: просто надрукуйте рядок, який повертає originalDoc.toXML()):

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

(Якщо ви хочете красиво відформатувати XML (із відступами та подачами рядків), використовуйте a Serializer; спасибі Пітеру Штібрані за те, що на це вказав.)

Тож слід визнати, що це не сильно відрізняється від рішення dom4j. :) Однак, з XOM може бути дещо приємніше працювати, тому що API краще задокументований, і завдяки його філософії дизайну існує один канонічний спосіб зробити кожну справу.

Додаток : Знову, ось як перетворити між org.w3c.dom.Documentі nu.xom.Document. Використовуйте допоміжні методи в DOMConverterкласі 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

Зверніть увагу, що замість new Builder (). Build (new StringReader ("<root> <given> </given> </root>")); Ви також можете використовувати new Builder (). build ("<root> <given> </given> </root>", "test.xml"); (де "test.xml" - це якийсь випадковий базовий URI)
Peter Štibraný

1
"Якщо ви хочете красиво відформатувати XML (із відступами та подачами рядків), я не впевнений, як це зробити за допомогою XOM." - за допомогою класу Serializer. Налаштуйте його, використовуючи setIndent і setMaxLength, і викличте запис (документ).
Peter Štibraný

Серіалізатор також легко налаштувати, підкласуючи.
Peter Štibraný

Дякую! Я насправді не розумів, що саме означає параметр baseURI; передача порожнього рядка також працює, тому я використовую це. У будь-якому випадку це дещо спрощує код. Що стосується форматування, Serializer справді чудово працює.
Jonik

Я думаю, що baseURI буде використовуватися для вирішення відносних посилань на DTD або XInclude ( lists.ibiblio.org/pipermail/xom-interest/2004-November/… )
Пітер Штібрані,

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

Якщо ви використовуєте dom4j, ви можете просто зробити:

Документ документа = DocumentHelper.parseText (текст);

(dom4j зараз знайдено тут: https://github.com/dom4j/dom4j )


Просто зайшов на їх веб-сайт. Вони розміщують Google Ads прямо на типовій навігаційній панелі, створеній Maven! Неймовірно!
Тіло

2
Очевидно, що сайт більше не експлуатується хлопцями dom4j, але деякі захопники доменів захопили ...
Тіло

1

... а якщо ви використовуєте суто XOM, щось на зразок цього:

    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 використовує fakeRoot внутрішньо, щоб робити майже те саме, тому він повинен бути безпечним, якщо не зовсім елегантним.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.