Sök…


Anmärkningar

XML-parsning är tolkningen av XML-dokument för att manipulera deras innehåll med förnuftiga konstruktioner, vare sig det är "noder", "attribut", "dokument", "namnutrymmen" eller händelser relaterade till dessa konstruktioner.

Java har ett inbyggt API för XML-dokumenthantering, kallad JAXP, eller Java API för XML-bearbetning . JAXP och en referensimplementering har gått med varje Java-version sedan Java 1.4 (JAXP v1.1) och har utvecklats sedan. Java 8 levereras med JAXP version 1.6.

API ger olika sätt att interagera med XML-dokument, som är:

  • DOM-gränssnittet (Document Object Model)
  • SAX-gränssnittet (Enkelt API för XML)
  • StAX-gränssnittet (Streaming API för XML)

Principer för DOM-gränssnittet

DOM-gränssnittet syftar till att tillhandahålla ett W3C DOM- kompatibelt sätt att tolka XML. Olika versioner av JAXP har stött olika DOM-specifikationer (upp till nivå 3).

Under gränssnittet för dokumentobjekt representeras ett XML-dokument som ett träd, börjar med "Dokumentelementet". Bastypen av API är Node typ, gör det möjligt att navigera från en Node till dess förälder, dess barn, eller dess syskon (även om inte alla Node s kan få barn, till exempel Text noder är finalen i trädet, och har aldrig barn). XML-taggar representeras som Element , vilket särskilt utvidgar Node med attributrelaterade metoder.

DOM-gränssnittet är väldigt användbart eftersom det tillåter en "en rad" analysering av XML-dokument som träd och gör det enkelt att modifiera det konstruerade trädet (nodtillägg, undertryckning, kopiering, ...) och slutligen dess serialisering (tillbaka till disken ) post ändringar. Detta kommer dock till ett pris: trädet ligger i minnet, DOM-träd är därför inte alltid praktiska för enorma XML-dokument. Dessutom är konstruktionen av trädet inte alltid det snabbaste sättet att hantera XML-innehåll, särskilt om man inte är intresserad av alla delar av XML-dokumentet.

Principer för SAX-gränssnittet

SAX API är ett händelsorienterat API för att hantera XML-dokument. Enligt denna modell tolkas komponenterna i ett XML-dokument som händelser (t.ex. "en tagg har öppnats", "en tagg har stängts", "en textnod har stött på", "en kommentar har stött på"). ..

SAX API använder en "push parsing" -metod, där en SAX Parser är ansvarig för att tolka XML-dokumentet och åberopar metoder för en delegat (en ContentHandler ) för att hantera vilken händelse som finns i XML-dokumentet. Vanligtvis skriver man aldrig en parser, men en tillhandahåller en hanterare för att samla all information som behövs från XML-dokumentet.

SAX-gränssnittet vinner begränsningar DOM gränssnittets genom att hålla endast de minsta nödvändiga data på tolken nivå (t.ex. namn sammanhang, validering state) därför endast information som hålls av ContentHandler - som du, utvecklaren är ansvarig - är hålls i minnet. Avvägningen är att det inte finns något sätt att "gå tillbaka i tiden / XML-dokumentet" med ett sådant tillvägagångssätt: medan DOM tillåter en Node att gå tillbaka till sin förälder finns det ingen sådan möjlighet i SAX.

Principer för StAX-gränssnittet

StAX API tar ett liknande tillvägagångssätt för att bearbeta XML som SAX API (det vill säga händelsedrivet), den enda väldigt betydande skillnaden är att StAX är en pull-parser (där SAX var en push-parser). I SAX har Parser kontrollen och använder återuppringningar på ContentHandler . I Stax ringer du parsaren och kontrollerar när / om du vill få nästa XML-händelse.

API börjar med XMLStreamReader (eller XMLEventReader ), som är de gateways genom vilka utvecklaren kan fråga nextEvent() , på en iteratorstil.

Analysera och navigera i ett dokument med DOM API

Med tanke på följande dokument:

<?xml version='1.0' encoding='UTF-8' ?>
<library>
   <book id='1'>Effective Java</book>
   <book id='2'>Java Concurrency In Practice</book>
</library>

Man kan använda följande kod för att bygga ett DOM-träd ur en 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");
}
}

Koden ger följande:

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

Analysera ett dokument med StAX API

Med tanke på följande 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>

Man kan använda följande kod för att analysera den och bygga en karta över boktitlar efter bok-id.

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);
}

Denna utgångar:

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}

I det här exemplet måste man vara försiktig med några saker:

  1. Användningen av xmlStreamReader.getAttributeValue fungerar eftersom vi först har kontrollerat att parsern är i START_ELEMENT . I evey andra tillstånd (utom ATTRIBUTES ) har parsaren mandat att kasta IllegalStateException , eftersom attribut bara kan visas i början av element.

  2. Samma sak gäller xmlStreamReader.getTextContent() , det fungerar eftersom vi är på en START_ELEMENT och vi vet i detta dokument att <book> -elementet inte har några icke-textbarnoder.

För mer komplexa dokumentparning (djupare, kapslade element, ...) är det en bra praxis att "delegera" parsern till undermetoder eller andra objets, t.ex. ha en BookParser klass eller -metod, och få den att hantera alla element från START_ELEMENT till END_ELEMENT för boken XML-taggen.

Man kan också använda ett Stack objekt för att hålla viktiga datas upp och ner i trädet.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow