Programmering

Kortlægning af XML til Java, del 1

XML er hot. Da XML er en form for selvbeskrivende data, kan den bruges til at indkode rige datamodeller. Det er let at se XML's værktøj som et dataudvekslingsmedium mellem meget forskellige systemer. Data kan let eksponeres eller offentliggøres som XML fra alle slags systemer: ældre COBOL-programmer, databaser, C ++ -programmer osv.

TEKSTBOKS:

TEXTBOX_HEAD: Kortlægning af XML til Java: Læs hele serien!

  • Del 1 - Brug SAX API til at kortlægge XML-dokumenter til Java-objekter
  • Del 2 - Opret et klassebibliotek, der bruger SAX API til at kortlægge XML-dokumenter til Java-objekter

: END_TEXTBOX

Brug af XML til at opbygge systemer udgør dog to udfordringer. For det første, mens generering af XML er en ligetil procedure, er den omvendte operation, der bruger XML-data fra et program, ikke. For det andet er de nuværende XML-teknologier lette at misbruge, hvilket kan efterlade en programmør med et langsomt hukommelses-sultent system. Faktisk kan tunge hukommelseskrav og lave hastigheder vise sig at være problematiske for systemer, der bruger XML som deres primære dataudvekslingsformat.

Nogle standardværktøjer, der i øjeblikket er tilgængelige til arbejde med XML, er bedre end andre. Specielt SAX API har nogle vigtige runtime-funktioner til præstationsfølsom kode. I denne artikel vil vi udvikle nogle mønstre til anvendelse af SAX API. Du vil være i stand til at oprette hurtig XML-til-Java-kortlægningskode med et minimum hukommelsesfodaftryk, selv for ret komplekse XML-strukturer (med undtagelse af rekursive strukturer).

I del 2 af denne serie vil vi dække anvendelsen af ​​SAX API på rekursive XML-strukturer, hvor nogle af XML-elementerne repræsenterer lister over lister. Vi vil også udvikle et klassebibliotek, der styrer navigationsaspekterne af SAX API. Dette bibliotek forenkler skrivning af XML-kortlægningskode baseret på SAX.

Kortlægningskode ligner kompileringskode

Skrivning af programmer, der bruger XML-data, er som at skrive en compiler. Det vil sige, de fleste compilere konverterer kildekode til et program, der kan køres i tre trin. Først en lexer modul grupperer tegn i ord eller tokens, som compileren genkender - en proces kendt som tokenizing. Et andet modul, kaldet parser, analyserer grupper af tokens for at genkende juridiske sprogkonstruktioner. Sidste, et tredje modul, den kode generator, tager et sæt juridiske sprogkonstruktioner og genererer eksekverbar kode. Undertiden blandes parsing og generering af kode.

For at bruge XML-data i et Java-program skal vi gennemgå en lignende proces. Først analyserer vi hvert tegn i XML-teksten for at genkende juridiske XML-tokens som startkoder, attributter, slutkoder og CDATA-sektioner.

For det andet kontrollerer vi, at tokens udgør juridiske XML-konstruktioner. Hvis et XML-dokument udelukkende består af juridiske konstruktioner i henhold til XML 1.0-specifikationen, er det det velformet. På det mest basale niveau er vi nødt til at sikre os, at f.eks. Hele taggningen har matchende åbnings- og lukemærker, og attributterne er korrekt struktureret i åbningskoden.

Også, hvis en DTD er tilgængelig, har vi muligheden for at sikre, at XML-konstruktioner, der findes under parsing, er lovlige med hensyn til DTD såvel som at være velformede XML.

Endelig bruger vi dataene i XML-dokumentet til at udrette noget nyttigt - jeg kalder denne kortlægning XML til Java.

XML-parsere

Heldigvis er der hyldekomponenter - XML-parsere - der udfører nogle af disse compiler-relaterede opgaver for os. XML-parsere håndterer alle leksikale analyser og parsingopgaver for os. Mange aktuelt tilgængelige Java-baserede XML-parsere understøtter to populære parsingsstandarder: SAX og DOM API'erne.

Tilgængeligheden af ​​en XML-parser uden hylde kan få det til at virke som den svære del af at bruge XML i Java er gjort for dig. I virkeligheden er anvendelse af en XML-parser uden hylde en involveret opgave.

SAX og DOM API'er

SAX API er hændelsesbaseret. XML-parsere, der implementerer SAX API, genererer hændelser, der svarer til forskellige funktioner, der findes i det parsede XML-dokument. Ved at svare på denne strøm af SAX-begivenheder i Java-kode kan du skrive programmer drevet af XML-baserede data.

DOM API er en objekt-model-baseret API. XML-parsere, der implementerer DOM, opretter en generisk objektmodel i hukommelsen, der repræsenterer indholdet af XML-dokumentet. Når XML-parseren er færdig med parsingen, indeholder hukommelsen et træ af DOM-objekter, der giver information om både strukturen og indholdet af XML-dokumentet.

DOM-konceptet voksede ud af HTML-browserverdenen, hvor en fælles dokumentobjektmodel repræsenterer HTML-dokumentet indlæst i browseren. Denne HTML DOM bliver derefter tilgængelig for scripting-sprog som JavaScript. HTML DOM har haft stor succes i denne applikation.

Farer ved DOM

Ved første øjekast ser DOM API ud til at være mere funktionsrig og derfor bedre end SAX API. DOM har dog alvorlige effektivitetsproblemer, der kan skade ydelsesfølsomme applikationer.

Den aktuelle gruppe af XML-parsere, der understøtter DOM, implementerer objektmodellen i hukommelsen ved at oprette mange små objekter, der repræsenterer DOM-noder, der indeholder enten tekst eller andre DOM-noder. Dette lyder naturligt nok, men har negative ydeevneimplikationer. En af de dyreste operationer i Java er ny operatør. Tilsvarende for alle ny operatør udført i Java, skal JVM-affaldssamleren til sidst fjerne objektet fra hukommelsen, når der ikke er nogen referencer til objektet. DOM API har tendens til virkelig at kaste JVM-hukommelsessystemet med dets mange små objekter, som typisk kastes til side kort efter parsing.

Et andet DOM-problem er, at det indlæser hele XML-dokumentet i hukommelsen. For store dokumenter bliver dette et problem. Igen, da DOM er implementeret så mange små objekter, er hukommelsens fodaftryk endnu større end selve XML-dokumentet, fordi JVM gemmer et par ekstra byte af information om alle disse objekter såvel som indholdet af XML-dokumentet.

Det er også foruroligende, at mange Java-programmer faktisk ikke bruger DOMs generiske objektstruktur. I stedet for, så snart DOM-strukturen indlæses i hukommelsen, kopierer de dataene til en objektmodel, der er specifik for et bestemt problemdomæne - en subtil men alligevel spildende proces.

Et andet subtilt problem for DOM API er, at koden, der er skrevet til den, skal scanne XML-dokumentet to gange. Det første pass opretter DOM-strukturen i hukommelsen, den anden lokaliserer alle XML-data, som programmet er interesseret i. Visse kodningsstilarter kan krydse DOM-strukturen flere flere gange, mens de forskellige stykker XML-data lokaliseres. Derimod tilskynder SAX's kodestil til at finde og indsamle XML-data i et enkelt pass.

Nogle af disse problemer kunne løses med et bedre underliggende datastrukturdesign til internt at repræsentere DOM-objektmodellen. Spørgsmål som tilskyndelse til flere behandlingskort og oversættelse mellem generiske og specifikke objektmodeller kan ikke behandles i XML-parserne.

SAX for overlevelse

Sammenlignet med DOM API er SAX API en attraktiv tilgang. SAX har ikke en generisk objektmodel, så den har ikke hukommelses- eller ydeevneproblemer forbundet med misbrug af ny operatør. Og med SAX er der ingen generisk objektmodel at ignorere, hvis du planlægger at bruge en bestemt problem-domæne-objektmodel i stedet. Da SAX desuden behandler XML-dokumentet i en enkelt passage, kræver det meget mindre behandlingstid.

SAX har nogle få ulemper, men de er for det meste relateret til programmøren, ikke API'ens runtime-ydeevne. Lad os se på nogle få.

Den første ulempe er konceptuel. Programmører er vant til at navigere for at hente data; for at finde en fil på en filserver navigerer du ved at ændre mapper. På samme måde skriver du en SQL-forespørgsel for de data, du har brug for, for at få data fra en database. Med SAX er denne model inverteret. Det vil sige, du opsætter kode, der lytter til listen over alle tilgængelige tilgængelige XML-data. Denne kode aktiveres kun, når der vises interessante XML-data. I første omgang virker SAX API underligt, men efter et stykke tid bliver tænkning på denne omvendte måde anden natur.

Den anden ulempe er farligere. Med SAX-kode vil den naive tilgang til "lad os tage et hack på det" komme hurtigt tilbage, fordi SAX-parseren navigerer udtømmende i XML-strukturen og samtidig levere de data, der er gemt i XML-dokumentet. De fleste mennesker fokuserer på datakortingsaspektet og forsømmer navigationsaspektet. Hvis du ikke adresserer direkte navigationsaspektet ved SAX-parsing, spredes koden, der holder styr på placeringen i XML-strukturen under SAX-parsing, og har mange subtile interaktioner. Dette problem svarer til dem, der er forbundet med overdreven afhængighed af globale variabler. Men hvis du lærer at strukturere SAX-kode korrekt for at forhindre, at den bliver uhåndterlig, er den mere ligetil end at bruge DOM API.

Grundlæggende SAX

Der er i øjeblikket to offentliggjorte versioner af SAX API. Vi bruger version 2 (se ressourcer) til vores eksempler. Version 2 bruger forskellige klasse- og metodenavne end version 1, men kodens struktur er den samme.

SAX er en API, ikke en parser, så denne kode er generisk på tværs af XML-parsere. For at få eksemplerne til at køre skal du få adgang til en XML-parser, der understøtter SAX v2. Jeg bruger Apache's Xerces parser. (Se ressourcer.) Gennemgå din parsers startvejledning for at få detaljer om at påberåbe sig en SAX-parser.

SAX API-specifikationen er ret ligetil. In indeholder mange detaljer, men dets primære opgave er at oprette en klasse, der implementerer ContentHandler interface, en tilbagekaldsgrænseflade, der bruges af XML-parsere til at underrette dit program om SAX-begivenheder, som de findes i XML-dokumentet.

SAX API leverer også bekvemt en Standardhåndterer implementeringsklasse for ContentHandler interface.

Når du har implementeret ContentHandler eller udvidet Standardhåndterer, behøver du kun at henvise XML-parseren til at analysere et bestemt dokument.

Vores første eksempel udvider Standardhåndterer for at udskrive hver SAX-begivenhed til konsollen. Dette giver dig en fornemmelse for, hvilke SAX-begivenheder der genereres, og i hvilken rækkefølge.

For at komme i gang er her eksemplet på XML-dokument, som vi bruger i vores første eksempel:

   Bob New York 

Dernæst ser vi kildekoden til XML-kortlægningskode i det første eksempel:

import org.xml.sax. *; import org.xml.sax.helpers. *; import java.io. *; public class Eksempel 1 udvider DefaultHandler {// Tilsidesæt metoder til DefaultHandler-klassen // for at få besked om SAX-begivenheder. // // Se org.xml.sax.ContentHandler for alle tilgængelige begivenheder. // public void startDocument () kaster SAXException {System.out.println ("SAX Event: START DOCUMENT"); } public void endDocument () kaster SAXException {System.out.println ("SAX Event: END DOCUMENT"); } public void startElement (String namespaceURI, String localName, String qName, Attribut attr) kaster SAXException {System.out.println ("SAX Event: START ELEMENT [" + localName + "]"); // Lad os også udskrive attributterne, hvis // der er nogen ... for (int i = 0; i <attr.getLength (); i ++) {System.out.println ("ATTRIBUTE:" + attr.getLocalName ( i) + "VALUE:" + attr.getValue (i)); }} public void endElement (String namespaceURI, String localName, String qName) kaster SAXException {System.out.println ("SAX Event: END ELEMENT [" + localName + "]"); } offentlige ugyldige tegn (char [] ch, int start, int længde) kaster SAXException {System.out.print ("SAX Event: CHARACTERS ["); prøv {OutputStreamWriter outw = ny OutputStreamWriter (System.out); outw.write (ch, start, længde); outw.flush (); } fange (Undtagelse e) {e.printStackTrace (); } System.out.println ("]"); } offentlig statisk ugyldig hoved (String [] argv) {System.out.println ("Eksempel1 SAX-begivenheder:"); prøv {// Opret SAX 2-parser ... XMLReader xr = XMLReaderFactory.createXMLReader (); // Indstil ContentHandler ... xr.setContentHandler (nyt eksempel1 ()); // Parse filen ... xr.parse (ny InputSource (ny FileReader ("Eksempel1.xml"))); } fange (Undtagelse e) {e.printStackTrace (); }}} 

Endelig er her output genereret ved at køre det første eksempel med vores XML-eksempeldokument:

Eksempel1 SAX begivenheder: SAX begivenhed: START DOKUMENT SAX begivenhed: START ELEMENT [enkel] ATTRIBUT: dato VÆRDI: 7/7/2000 SAX begivenhed: KARAKTERER [] SAX begivenhed: START ELEMENT [navn] SAX begivenhed: KARAKTERER [Bob] SAX begivenhed : END ELEMENT [navn] SAX begivenhed: KARAKTERER [] SAX begivenhed: START ELEMENT [placering] SAX begivenhed: KARAKTERER [New York] SAX begivenhed: END ELEMENT [placering] SAX begivenhed: KARAKTERER [] SAX begivenhed: SLUT ELEMENT [enkel] SAX-begivenhed: SLUTDOKUMENT 

Som du kan se, kalder SAX-parseren det relevante ContentHandler metode til hver SAX-begivenhed, den opdager i XML-dokumentet.

Hej Verden

Nu hvor vi forstår det grundlæggende mønster af SAX, kan vi begynde at gøre noget lidt nyttigt: udtræk værdier fra vores enkle XML-dokument og demonstrer det klassiske hej verdensprogram.