Java Language
Analiza XML przy użyciu interfejsów API JAXP
Szukaj…
Uwagi
Parsowanie XML to interpretacja dokumentów XML w celu manipulowania ich zawartością przy użyciu rozsądnych konstrukcji, takich jak „węzły”, „atrybuty”, „dokumenty”, „przestrzenie nazw” lub zdarzenia związane z tymi konstrukcjami.
Java ma natywny interfejs API do obsługi dokumentów XML, zwany JAXP lub Java API do przetwarzania XML . JAXP i implementacja referencyjna zostały dołączone do każdej wersji Java od wersji Java 1.4 (JAXP v1.1) i od tego czasu ewoluowały. Java 8 dostarczana z JAXP w wersji 1.6.
Interfejs API zapewnia różne sposoby interakcji z dokumentami XML, którymi są:
- Interfejs DOM (Document Object Model)
- Interfejs SAX (Simple API for XML)
- Interfejs StAX (Streaming API for XML)
Zasady interfejsu DOM
Interfejs DOM ma na celu zapewnienie zgodnego z W3C sposobu interpretacji XML. Różne wersje JAXP obsługują różne poziomy specyfikacji DOM (do poziomu 3).
W interfejsie Document Object Model dokument XML jest reprezentowany jako drzewo, zaczynając od „elementu dokumentu”. Typ podstawy API jest Node
typu, pozwala poruszać się z Node
do jego rodziców, jego dzieci, lub jego rodzeństwa (choć nie wszystkie Node
s może mieć dzieci, na przykład, Text
węzły są ostateczne w drzewie, i nigdy nie mieć dzieci). Znaczniki XML są reprezentowane jako Element
S, w szczególności rozszerzenie Node
z odpowiednich metod na atrybutach.
Interfejs DOM jest bardzo przydatny, ponieważ umożliwia parsowanie dokumentów XML w postaci drzew i pozwala na łatwą modyfikację skonstruowanego drzewa (dodawanie węzłów, eliminowanie, kopiowanie, ...) i wreszcie jego serializację (z powrotem na dysk) ) modyfikacje wpisów. Ma to jednak swoją cenę: drzewo pozostaje w pamięci, dlatego drzewa DOM nie zawsze są praktyczne w przypadku dużych dokumentów XML. Co więcej, konstrukcja drzewa nie zawsze jest najszybszym sposobem radzenia sobie z zawartością XML, szczególnie jeśli nie interesują nas wszystkie części dokumentu XML.
Zasady interfejsu SAX
Interfejs API SAX jest interfejsem API zorientowanym na zdarzenia do obsługi dokumentów XML. W tym modelu składniki dokumentów XML są interpretowane jako zdarzenia (np. „Znacznik został otwarty”, „znacznik został zamknięty”, „napotkano węzeł tekstowy”, „napotkano komentarz”). ..
Interfejs API SAX wykorzystuje metodę „parsowania”, w której Parser
SAX jest odpowiedzialny za interpretację dokumentu XML i wywołuje metody na delegacie ( ContentHandler
) w celu obsługi dowolnego zdarzenia znalezionego w dokumencie XML. Zwykle nigdy nie pisze się parsera, ale zapewnia się moduł obsługi, który zbiera wszystkie potrzebne informacje z dokumentu XML.
Interfejs SAX pokonuje ograniczenia interfejsu DOM, przechowując tylko minimalne niezbędne dane na poziomie parsera (np. Konteksty przestrzeni nazw, stan sprawdzania poprawności), dlatego tylko informacje przechowywane przez ContentHandler
- za które odpowiedzialny jest użytkownik - programista - są przechowywane w pamięci. Kompromis polega na tym, że przy takim podejściu nie ma możliwości „cofnięcia się w czasie / dokument XML”: podczas gdy DOM umożliwia Node
powrót do jego elementu nadrzędnego, w SAX nie ma takiej możliwości.
Zasady interfejsu StAX
Interfejs API StAX ma podobne podejście do przetwarzania XML, jak interfejs API SAX (tzn. Sterowany zdarzeniami), jedyną bardzo znaczącą różnicą jest to, że StAX jest analizatorem składni pull (gdzie SAX był analizatorem push). W SAX Parser
jest pod kontrolą i wykorzystuje wywołania zwrotne w ContentHandler
. W Stax wywołujesz parser i kontrolujesz kiedy / jeśli chcesz uzyskać następne „zdarzenie” XML.
Interfejs API zaczyna się od XMLStreamReader (lub XMLEventReader ), które są bramami, przez które programista może zapytać nextEvent()
w sposób iteracyjny.
Analiza i nawigacja po dokumencie za pomocą interfejsu API DOM
Biorąc pod uwagę następujący dokument:
<?xml version='1.0' encoding='UTF-8' ?>
<library>
<book id='1'>Effective Java</book>
<book id='2'>Java Concurrency In Practice</book>
</library>
Można użyć następującego kodu do zbudowania drzewa DOM z pomocą String
:
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;
public class DOMDemo {
public static void main(String[] args) throws Exception {
String xmlDocument = "<?xml version='1.0' encoding='UTF-8' ?>"
+ "<library>"
+ "<book id='1'>Effective Java</book>"
+ "<book id='2'>Java Concurrency In Practice</book>"
+ "</library>";
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// This is useless here, because the XML does not have namespaces, but this option is usefull to know in cas
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
// There are various options here, to read from an InputStream, from a file, ...
Document document = documentBuilder.parse(new InputSource(new StringReader(xmlDocument)));
// Root of the document
System.out.println("Root of the XML Document: " + document.getDocumentElement().getLocalName());
// Iterate the contents
NodeList firstLevelChildren = document.getDocumentElement().getChildNodes();
for (int i = 0; i < firstLevelChildren.getLength(); i++) {
Node item = firstLevelChildren.item(i);
System.out.println("First level child found, XML tag name is: " + item.getLocalName());
System.out.println("\tid attribute of this tag is : " + item.getAttributes().getNamedItem("id").getTextContent());
}
// Another way would have been
NodeList allBooks = document.getDocumentElement().getElementsByTagName("book");
}
}
Kod daje następujące wyniki:
Root of the XML Document: library
First level child found, XML tag name is: book
id attribute of this tag is : 1
First level child found, XML tag name is: book
id attribute of this tag is : 2
Analiza dokumentu za pomocą interfejsu API StAX
Biorąc pod uwagę następujący dokument:
<?xml version='1.0' encoding='UTF-8' ?>
<library>
<book id='1'>Effective Java</book>
<book id='2'>Java Concurrency In Practice</book>
<notABook id='3'>This is not a book element</notABook>
</library>
Można użyć następującego kodu, aby go przeanalizować i zbudować mapę tytułów książek według identyfikatora książki.
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
public class StaxDemo {
public static void main(String[] args) throws Exception {
String xmlDocument = "<?xml version='1.0' encoding='UTF-8' ?>"
+ "<library>"
+ "<book id='1'>Effective Java</book>"
+ "<book id='2'>Java Concurrency In Practice</book>"
+ "<notABook id='3'>This is not a book element </notABook>"
+ "</library>";
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
// Various flavors are possible, e.g. from an InputStream, a Source, ...
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xmlDocument));
Map<Integer, String> bookTitlesById = new HashMap<>();
// We go through each event using a loop
while (xmlStreamReader.hasNext()) {
switch (xmlStreamReader.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
System.out.println("Found start of element: " + xmlStreamReader.getLocalName());
// Check if we are at the start of a <book> element
if ("book".equals(xmlStreamReader.getLocalName())) {
int bookId = Integer.parseInt(xmlStreamReader.getAttributeValue("", "id"));
String bookTitle = xmlStreamReader.getElementText();
bookTitlesById.put(bookId, bookTitle);
}
break;
// A bunch of other things are possible : comments, processing instructions, Whitespace...
default:
break;
}
xmlStreamReader.next();
}
System.out.println(bookTitlesById);
}
To daje:
Found start of element: library
Found start of element: book
Found start of element: book
Found start of element: notABook
{1=Effective Java, 2=Java Concurrency In Practice}
W tej próbce należy zwrócić uwagę na kilka rzeczy:
Zastosowanie
xmlStreamReader.getAttributeValue
działa, ponieważ najpierw sprawdziliśmy, czy analizator składni jest w stanieSTART_ELEMENT
. W każdym innym stanie (opróczATTRIBUTES
) analizator jest upoważniony doIllegalStateException
, ponieważ atrybuty mogą pojawiać się tylko na początku elementów.to samo dotyczy
xmlStreamReader.getTextContent()
, działa, ponieważ jesteśmy wSTART_ELEMENT
i wiemy w tym dokumencie, że element<book>
nie ma nietekstowych węzłów potomnych.
W przypadku bardziej skomplikowanego analizowania dokumentów (głębsze, zagnieżdżone elementy, ...) dobrą praktyką jest „delegowanie” parsera do pod-metod lub innych obiektów, np. BookParser
klasy lub metody BookParser
i zajmowanie się każdym elementem od START_ELEMENT do END_ELEMENT tagu XML książki.
Można również użyć obiektu Stack
aby przechodzić wokół ważnych danych w górę iw dół drzewa.