Jak załadować org.w3c.dom.Document z XML w ciągu?

103

Mam kompletny dokument XML w postaci ciągu i chciałbym mieć Documentobiekt. Google wyrzuca różnego rodzaju śmieci. Jakie jest najprostsze rozwiązanie? (W Javie 1.5)

Rozwiązanie Dzięki Mattowi McMinnowi zdecydowałem się na tę implementację. Ma dla mnie odpowiedni poziom elastyczności wprowadzania danych i szczegółowości wyjątków. (Dobrze jest wiedzieć, czy błąd pochodzi ze zniekształconego kodu XML - SAXException- czy po prostu złego IO - IOException.)

public static org.w3c.dom.Document loadXMLFrom(String xml)
    throws org.xml.sax.SAXException, java.io.IOException {
    return loadXMLFrom(new java.io.ByteArrayInputStream(xml.getBytes()));
}

public static org.w3c.dom.Document loadXMLFrom(java.io.InputStream is) 
    throws org.xml.sax.SAXException, java.io.IOException {
    javax.xml.parsers.DocumentBuilderFactory factory =
        javax.xml.parsers.DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    javax.xml.parsers.DocumentBuilder builder = null;
    try {
        builder = factory.newDocumentBuilder();
    }
    catch (javax.xml.parsers.ParserConfigurationException ex) {
    }  
    org.w3c.dom.Document doc = builder.parse(is);
    is.close();
    return doc;
}
Frank Krueger
źródło
Byłoby miło, gdybyś mógł poprawić rozwiązanie. Używanie String.getByptes i InputStream narzuca problemy i18n. Jeden z moich znajomych dostał stąd kod, który jest nieprawidłowy. Na szczęście, że findbugs wykrył problem. Prawidłowym rozwiązaniem dostarczonym przez Erickson jest użycie InputSource.
Kenneth Xu

Odpowiedzi:

80

To działa dla mnie w Javie 1.5 - usunąłem określone wyjątki dla czytelności.

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import java.io.ByteArrayInputStream;

public Document loadXMLFromString(String xml) throws Exception
{
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    factory.setNamespaceAware(true);
    DocumentBuilder builder = factory.newDocumentBuilder();

    return builder.parse(new ByteArrayInputStream(xml.getBytes()));
}
Matt McMinn
źródło
28
Jak zauważono w odpowiedzi Sylvarkinga, ten kod używa getBytes()bez uwzględnienia kodowania.
McDowell
2
masz na myśli odpowiedź Ericksona? a może zmienił nazwę swojego profilu?
rogerdpack
1
czy nie powinno być castingu return (Document) builder.parse(new ByteArrayInputStream(xml.getBytes()));?
InfantPro'Aravind '16
150

Hej!

Istnieje potencjalnie poważny problem z tym kodem, ponieważ ignoruje on kodowanie znaków określone w String(którym jest domyślnie UTF-8). Gdy wywołujesz String.getBytes(), domyślne kodowanie platformy jest używane do kodowania znaków Unicode na bajty. Zatem parser może pomyśleć, że pobiera dane UTF-8, podczas gdy w rzeczywistości pobiera EBCDIC lub coś… nieładne!

Zamiast tego użyj metody analizy, która pobiera InputSource, które można skonstruować za pomocą czytnika, na przykład:

import java.io.StringReader;
import org.xml.sax.InputSource;

        return builder.parse(new InputSource(new StringReader(xml)));

To może nie wydawać się wielkim problemem, ale nieznajomość problemów z kodowaniem znaków prowadzi do podstępnego gnicia kodu, podobnego do y2k.

erickson
źródło
3
Takie proste, ale tak nieuchwytne rozwiązanie w Google. Dziękuję +1
pat8719
6
Zdaję sobie teraz sprawę, że powinienem nie tylko skopiować i wkleić zaakceptowaną odpowiedź, ale raczej przeczytać ją.
Witalij Sazanowicz
1
Niesamowite! Uratowaliśmy nasze życie na JDK8 za pomocą następującego pliku instalacyjnego. Encoding = ISO-8859_1, javax.servlet.request.encoding = UTF-8 PS Odpowiedź oznaczona jako poprawna nie zadziałała dla nas
kosta5
9

Właśnie miałem podobny problem, z wyjątkiem tego, że potrzebowałem NodeList, a nie dokumentu, oto co wymyśliłem. Jest to w większości to samo rozwiązanie, co poprzednio, ulepszone, aby usunąć element główny jako NodeList i wykorzystując sugestię Ericksona, aby zamiast tego użyć InputSource do problemów z kodowaniem znaków.

private String DOC_ROOT="root";
String xml=getXmlString();
Document xmlDoc=loadXMLFrom(xml);
Element template=xmlDoc.getDocumentElement();
NodeList nodes=xmlDoc.getElementsByTagName(DOC_ROOT);

public static Document loadXMLFrom(String xml) throws Exception {
        InputSource is= new InputSource(new StringReader(xml));
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = null;
        builder = factory.newDocumentBuilder();
        Document doc = builder.parse(is);
        return doc;
    }
shsteimer
źródło
1

Aby manipulować XML w Javie, zawsze używam Transformer API:

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;

public static Document loadXMLFrom(String xml) throws TransformerException {
    Source source = new StreamSource(new StringReader(xml));
    DOMResult result = new DOMResult();
    TransformerFactory.newInstance().newTransformer().transform(source , result);
    return (Document) result.getNode();
}   
Xavier Dury
źródło