XML in own applications Patryk Czarnik Institute of Informatics University of Warsaw XML and Modern Techniques of Content Management 2010/11 Introduction XML in own applications Models Generyczne drzewo dokumentu (DOM) XML binding (JAXB) Model zdarzeniowy (SAX) Model strumieniowy (StAX) Porównanie Obsługa standardów około-xml-owych Walidacja Transformacje
XML in own applications what for? To access data in XML format To use XML as data carrier (storage and transmission) To support XML applications (Web, content management,... ) To make use of XML-related standards (XML Schema, XInclude, XSLT, XQuery,... ) To make use of XML-based software technology (Web Services etc.) XML in own applications how? Bad way Treat XML as plain text files and write low-level XML support from scratch Better approach Use existing libraries and tools Even better Use standardised interfaces independent from particular suppliers
XML in Java Java motivation for us One of the most popular programming languages Very good XML support Standards Both included in Java Standard Edition from 6.0 Java API for XML Processing (JAXP) many interfaces and few classes, "factories" and pluggability layer support for XML parsing and serialisation (DOM, SAX, StAX) support for XInclude, XML Schema, XPath, XSLT Java API for XML Binding (JAXB) binding between Java objects and XML documents Classification of XML access models Document read into memory generic interface example: DOM interface depending on document type (schema) example: JAXB Document processed node by node event model (push parsing) example: SAX streaming model (pull parsing) example: StAX
Document object model Whole document in memory, as tree of objects W3C Recommendations DOM Level 1 1998 DOM Level 3 2004 Many (specification) modules Dom Core the most important DOM module for XML DOM important interfaces Node NodeList NamedNodeMap Element Attr Entity Document Processing Instruction CharacterData Entity Reference DocumentFragment Comment Text DocumentType CDATASection
Interfejs Node wybrane metody Content access getattributes() getchildnodes() getfirstchild() getlastchild() getnextsibling() getprevioussibling() getnodename() getnodevalue() getnodetype() getownerdocument() getparentnode() haschildnodes() Content modification appendchild(node) insertbefore(node, Node) removechild(node) replacechild(node, Node) setnodevalue(string) setnodename(string) Copying clonenode(boolean) Example introduction Example document <?xml version="1.0"?> <numbers> <group important="yes"> <l>52 </l><s>25</s> </group> <group important="no"> <l>5</l><l>21</l> </group> <group important="yes"> <s>9</s><l>12 </l> </group> </numbers> Problem Count sum of element l values from important groups.
DOM example (1) Program i n t r e s u l t = 0 ; D o c u m e n t B u i l d e r F a c t o r y f a c t o r y = D o c u m e n t B u i l d e r F a c t o r y. n e w I n s t a n c e ( ) ; f a c t o r y. s e t V a l i d a t i n g ( t rue ) ; DocumentBuilder b u i l d e r = f a c t o r y. newdocumentbuilder ( ) ; Document doc = b u i l d e r. p a r s e ( a r g s [ 0 ] ) ; Node c u r = doc. g e t F i r s t C h i l d ( ) ; while ( c u r. getnodetype ( )!= Node.ELEMENT_NODE) { c u r = c u r. g e t N e x t S i b l i n g ( ) ; c u r = c u r. g e t F i r s t C h i l d ( ) ; while ( c u r!= n u l l ) { i f ( c u r. getnodetype ( ) == Node.ELEMENT_NODE) { S t r i n g a t t V a l = c u r. g e t A t t r i b u t e s ( ). getnameditem ( " i m p o r t a n t " ). getnodevalue ( ) ; i f ( " yes ". e q u a l s ( a t t V a l ) { r e s u l t += p r o c e s s G r o u p ( c u r ) ; c u r = c u r. g e t N e x t S i b l i n g ( ) ; DOM example (2) Method processgroup p r i v a t e s t a t i c i n t p r o c e s s G r o u p ( Node group ) { i n t r e s u l t = 0 ; Node c u r = group. g e t F i r s t C h i l d ( ) ; while ( c u r!= n u l l ) { i f ( c u r. getnodetype ( ) == Node.ELEMENT_NODE && c u r. getnodename ( ). e q u a l s ( " l " ) ) { S t r i n g B u f f e r buf = new S t r i n g B u f f e r ( ) ; Node c h i l d = c u r. g e t F i r s t C h i l d ( ) ; while ( c h i l d!= n u l l ) { i f ( c h i l d. getnodetype ( ) == Node. TEXT_NODE) buf. append ( c h i l d. getnodevalue ( ) ) ; c h i l d = c h i l d. g e t N e x t S i b l i n g ( ) ; r e s u l t += I n t e g e r. p a r s e I n t ( buf. t o S t r i n g ( ) ) ; ; c u r = c u r. g e t N e x t S i b l i n g ( ) ; return r e s u l t ;
XML binding idea XML documents and objects (e.g. Java): schema (type) corresponds to class, document/node corresponds to object. Implementations: JAXB (Sun), Castor (Exolab), Dynamic XML (Object Space). Java API for XML Binding (JAXB) Inspired by Sun, supported by java.net Current version: 2.0 Motivated by Web Services Components generic API fragment two-way translation specification customisation and annotations
JAXB usage scenarios Schema to Java 1. Schema preparation 2. Schema to Java compilation (XJC) result: annotated classes basing on schema types transalation customisable 3. Developing application making use of generic part of JAXB API generated classes Java to Schema 1. Model classes preparation 2. Adding annotations classes 3. Making use of generic JAXB API to perform marchalling, unmarchalling etc. 4. (Optional) Generating schema basing on classes JAXB example(1) Schema and generated classes Schema <xs:element name="numbers"> <xs:complextype> <xs:sequence> <xs:element ref="group" minoccurs="0" maxoccurs="unbounded"/> </xs:sequence> </xs:complextype> </xs:element> <xs:element name="l" type="xs:integer"/> Generated classes Numbers List<Group> getgroup() Corresponding classes JAXBElement<BigInteger> QName getname() BigInteger getvalue() void setvalue(biginteger)... <xs:element name="s" type="xs:integer"/>
JAXB example (2) Schema and generated classes Schema <xs:element name="group"> <xs:complextype> <xs:choice minoccurs="0" maxoccurs="unbounded"> <xs:element ref="l"/> <xs:element ref="s"/> </xs:choice> <xs:attribute name="important"> <xs:simpletype> <xs:restriction base="xs:string"> <xs:enumeration value="yes"/> <xs:enumeration value="no"/> </xs:restriction> </xs:simpletype> </xs:attribute> </xs:complextype> </xs:element> Generated classes Group List<JAXBElement <BigInteger> > getlors() String getimportant() void setimportant(string) JAXB example(3) Program i n t r e s u l t = 0 ; JAXBContext j c = JAXBContext. n e w I n s t a n c e ( " j a x b _ g e n e r a t e d " ) ; U n m a r s h a l l e r u = j c. c r e a t e U n m a r s h a l l e r ( ) ; Liczby doc = ( Liczby ) u. unmarshal ( new F i l e I n p u t S t r e a m ( a r g s [ 0 ] ) ) ; L i s t <Group > g r o u p s = doc. getgroup ( ) ; f o r ( Group group : g r o u p s ) { i f ( " t a k ". e q u a l s ( grupa. g e t I m p o r t a n t ( ) ) ) { r e s u l t += p r o c e s s G r o u p ( group ) ;
JAXB example (4) Method processgroup p r i v a t e s t a t i c i n t p r o c e s s G r o u p ( Group group ) { i n t r e s u l t = 0 ; L i s t <JAXBElement < B i g I n t e g e r >> elems = group. getlors ( ) ; f o r ( JAXBElement < B i g I n t e g e r > elem : elems ) { i f ( " l ". e q u a l s ( elem. getname ( ). g e t L o c a l P a r t ( ) ) ) { B i g I n t e g e r v a l = elem. g e t V a l u e ( ) ; r e s u l t += v a l. i n t V a l u e ( ) ; return r e s u l t ; Model zdarzeniowy idea Umożliwienie programiście podania dowolnego kodu, wykonywanego podczas czytania dokumentu: dokument XML jako ciąg zdarzeń (np. początek elementu, węzeł tekstowy, koniec dokumentu,... ), procedury podane przez programistę wykonywane w odpowiedzi na zdarzenia różnego typu, treść dokumentu przekazywana w parametrach. Po stronie parsera: analiza leksykalna, kontrola poprawności składniowej, opcjonalnie walidacja. Możliwe realizacje w zależności od języka programowania: obiekt ( handler ) zawierający zestaw metod wykonywanych przy okazji różnych zdarzeń (języki obiektowe), funkcje (języki funkcyjne), wskaźniki do funkcji (C).
Simple API for XML (SAX) Standard odpowiedni dla języków obiektowych. wzorcowe interfejsy zapisane w Javie SAX jak używać? (1) Kroki implementacji 1. Klasa implementująca interfejs ContentHandler. 2. Opcjonalnie klasa implementująca interfejsy ErrorHandler, DTDHandler, EntityResolver. jedna klasa może implementować wszystkie te interfejsy, możemy w tym celu rozszerzyć klasę DefaultHandler, która zawiera puste implementacje wszystkich wymaganych metod.
SAX jak używać? (2) Schemat typowej aplikacji 1. Pobranie obiektu XMLReader z fabryki. 2. Stworzenie obiektu "handlera". 3. Rejestracja handlera w parserze (XMLReader) metodami setcontenthandler, seterrorhandler itp. 4. Wywołanie metody parse. 5. Wykonanie (przez parser) naszego kodu z handlera. 6. Wykorzystanie danych zebranych przez handler. SAX przykład (1) Implementacja ContentHandler-a p r i v a t e s t a t i c c l a s s NumbersHandler extends D e f a u l t H a n d l e r { enum S t a t e {OUT, GROUP, L ; p r i v a t e i n t r e s u l t = 0 ; p r i v a t e S t a t e s t a t e = S t a t e.out; p r i v a t e S t r i n g B u f f e r buf ; p u b l i c i n t g e t R e s u l t ( ) { return r e s u l t ; p u b l i c void s t a r t E l e m e n t ( S t r i n g u r i, S t r i n g localname, S t r i n g qname, A t t r i b u t e s a t t r i b u t e s ) throws SAXException { i f ( " group ". e q u a l s ( qname ) ) { S t r i n g a t t r V a l = a t t r i b u t e s. g e t V a l u e ( " i m p o r t a n t " ) ; i f ( " yes ". e q u a l s ( a t t r V a l ) ) s t a t e = S t a t e.group; e l s e i f ( " l ". e q u a l s ( qname ) ) { i f ( s t a n == S t a t e.group) { s t a t e = Stan. L ; buf = new S t r i n g B u f f e r ( ) ;
SAX przykład (2) Ciąg dalszy LiczbyHandler p u b l i c void c h a r a c t e r s ( char [ ] ch, i n t s t a r t, i n t l e n g t h ) throws SAXException { i f ( s t a n == Stan. L ) buf. append ( ch, s t a r t, l e n g t h ) ; p u b l i c void endelement ( S t r i n g u r i, S t r i n g localname, S t r i n g qname ) throws SAXException { i f ( " group ". e q u a l s ( qname ) ) { i f ( s t a t e == S t a t e.grupa) { s t a t e = S t a t e.zewn; e l s e i f ( " l ". e q u a l s ( qname ) ) { i f ( s t a t e == S t a t e. LICZBA ) { s t a t e = S t a t e.grupa; r e s u l t += I n t e g e r. p a r s e I n t ( buf. t o S t r i n g ( ) ) ; / NumbersHandler / SAX przykład (3) Program SAXParserFactory f a c t o r y = SAXParserFactory. n e w I n s t a n c e ( ) ; f a c t o r y. s e t V a l i d a t i n g ( t rue ) ; SAXParser p a r s e r = f a c t o r y. newsaxparser ( ) ; L i c z b y H a n d l e r h a n d l e r = new NumbersHandler ( ) ; p a r s e r. p a r s e ( a r g s [ 0 ], h a n d l e r ) ; System. o u t. p r i n t l n ( "Sum : "+ h a n d l e r. g e t R e s u l t ( ) ) ;
Filtry SAX Implementują interfejs XMLFilter, a także (pośrednio) XMLReader zachowują się jak parser, ale ich źródłem danych jest inny XMLReader (parser lub filtr). można je łączyć w łańcuchy. Domyślna implementacja: XMLFilterImpl: przepuszcza wszystkie zdarzenia, implementuje interfejsy ContentHandler, ErrorHandler itp. Filtry pozwalają na: filtrowanie zdarzeń, zmianę danych (a nawet struktury) dokumentu przed wysłaniem zdarzenia dalej, przetwarzanie dokumentu przez wiele modułów podczas jednego parsowania. Filtr SAX przykład p u b l i c c l a s s L i c z b y F i l t r extends XMLFilterImpl { p r i v a t e boolean c z y P r z e p u s z c z a c = t rue ; p u b l i c void c h a r a c t e r s ( char [ ] ach, i n t a S t a r t, i n t alength ) throws SAXException { i f ( c z y P r z e p u s z c z a c ) super. c h a r a c t e r s ( ach, a S t a r t, alength ) ; p u b l i c void endelement ( S t r i n g auri, S t r i n g alocalname, S t r i n g aname ) throws SAXException { i f ( c z y P r z e p u s z c z a c ) super. endelement ( auri, alocalname, aname ) ; i f ( " grupa ". e q u a l s ( aname ) ) c z y P r z e p u s z c z a c = t rue ; p u b l i c void s t a r t E l e m e n t ( S t r i n g auri, S t r i n g alocalname, S t r i n g aname, A t t r i b u t e s a t t s ) throws SAXException { i f ( " grupa ". e q u a l s ( aname ) && " n i e ". e q u a l s ( a t t s. g e t V a l u e ( " wazne " ) ) ) c z y P r z e p u s z c z a c = f a l s e ; i f ( c z y P r z e p u s z c z a c ) super. s t a r t E l e m e n t ( auri, alocalname, aname, a t t s ) ;
Model strumieniowy (pull parsing) Alternatywa dla modelu zdarzeniowego: aplikacja "wyciąga" kolejne zdarzenia z parsera, przetwarzanie kontrolowane przez aplikację, a nie parser, parser działa podobnie jak iterator, kursor lub strumień danych, Zachowane cechy modelu SAX: duża wydajność, możliwość przetwarzania dowolnie dużych dokumentów. Standaryzacja: Common XmlPull API, Java Community Process, JSR 173: Streaming API for XML. Pull parsing korzyści Jeszcze większa wydajność niż w (i tak już wydajnym) modelu SAX, dzięki: możliwości przerwania przetwarzania przed końcem pliku, gdy potrzebujemy z niego tylko część danych, możliwości zmniejszenia liczby kopiowań obiektów typu String, szybszemu filtrowaniu zdarzeń. Możliwość prostej obróbki wielu dokumentów jednocześnie. Bardziej proceduralny styl programowania, co daje: mniej stanów do pamiętania, możliwość użycia rekursji, zwiększone powtórne użycie kodu. Źródło: M. Plechawski, "Nie pozwól się popychać", Software 2.0, 6/2003
StAX (dawniej Sun Java Streaming XML Parser) Standard parserów strumieniowych dla Javy (Sun). Realizacja założeń dokumentu JSR 173, zawarty w JSE 6.0. Najważniejsze interfejsy XMLStreamReader: hasnext(), int next(), int geteventtype(), getname(), getvalue(), getattributevalue(),... XMLEventReader: XMLEvent next(), XMLEvent peek(), XMLEvent: geteventtype(), isstartelement(), ischaracters(),... podinterfejsy StartElement, Characters,... XMLStreamWriter, XMLEventWriter, XMLStreamFilter, XMLEventFilter. StAX przykład (1) Program p r i v a t e s t a t i c XMLStreamReader f R e a d e r ; p u b l i c void run ( S t r i n g [ ] a r g s ) { i n t r e s u l t = 0 ; XMLInputFactory f a c t o r y = XMLInputFactory. n e w I n s t a n c e ( ) ; f R e a d e r = f a c t o r y. createxmlstreamreader ( new F i l e I n p u t S t r e a m ( a r g s [ 0 ] ) ) ; while ( f R e a d e r. hasnext ( ) ) { i n t eventtype = f R e a d e r. n e x t ( ) ; i f ( eventtype == XMLStreamConstants. START_ELEMENT) { i f ( " group ". e q u a l s ( f R e a d e r. getlocalname ( ) ) ) { S t r i n g a t t r V a l = f R e a d e r. g e t A t t r i b u t e V a l u e ( null, " i m p o r t a n t " ) ; i f ( " yes ". e q u a l s ( a t t r V a l ) ) { r e s u l t += t h i s. p r o c e s s G r o u p ( ) ; f R e a d e r. c l o s e ( ) ; System. o u t. p r i n t l n ( " R e s u l t : "+ r e s u l t ) ;
StAX przykład (2) Metoda processgroup p r i v a t e i n t p r o c e s s G r o u p ( ) throws XMLStreamException { i n t r e s u l t = 0 ; while ( f R e a d e r. hasnext ( ) ) { i n t eventtype = f R e a d e r. n e x t ( ) ; switch ( eventtype ) { c a s e XMLStreamConstants. START_ELEMENT : i f ( " l ". e q u a l s ( f R e a d e r. getlocalname ( ) ) ) { S t r i n g v a l = f R e a d e r. g e t E l e m e n t T e x t ( ) ; r e s u l t += I n t e g e r. p a r s e I n t ( v a l ) ; break ; c a s e XMLStreamConstants. END_ELEMENT : i f ( " group ". e q u a l s ( f R e a d e r. getlocalname ( ) ) ) { return r e s u l t ; break ; return r e s u l t ; Which model to choose? (1) Document tree in memory: small documents (must fit in memory) concurrent access to many nodes creating and editing document "in place" Generyczny model dokumentu (np. DOM): nieznana/niedoprecyzowana struktura dokumentów, dopuszczalna niższa efektywność. Wiązanie XML (np. JAXB): ustalona i znana struktura dokumentu (Schema/DTD), zapisywanie do XML obiektów z aplikacji (np. wymiana danych).
Which model to choose? (2) Processing node by node potentially large documents relatively simple, local operations efficiency is the key factor Model zdarzeniowy (np. SAX): filtrowanie zdarzeń, asynchroniczne napływanie zdarzeń, kilka rodzajów przetwarzania podczas jednego czytania dokumentu. Przetwarzanie strumieniowe (np. StAX): koniec przetwarzania po wystąpieniu poszukiwanych danych, przetwarzanie zdarzenia zależy od kontekstu (np. od tego, czy jesteśmy wewnątrz pewnego elementu), przetwarzanie równolegle więcej niż jednego pliku. Walidacja względem DTD podczas parsowania SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setvalidating(true); XMLReader reader = factory.newsaxparser().getxmlreader(); reader.setcontenthandler(mojconenthandler); reader.seterrorhandler(mojerrorhandler); reader.parse(args[0]);
Walidacja względem XML Schema SchemaFactory schemafactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schemat = schemafactory.newschema(new StreamSource(args[1])); SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setvalidating(false); factory.setschema(schemat); factory.setnamespaceaware(true); XMLReader reader = factory.newsaxparser().getxmlreader(); reader.setcontenthandler(mojconenthandler); reader.seterrorhandler(mojerrorhandler); reader.parse(args[0]); Walidacja i zapis drzewa DOM SchemaFactory schemafactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schemat = schemafactory.newschema(new StreamSource(args[1])); Validator validator = schemat.newvalidator(); validator.validate(new DOMSource(doc)); DOMImplementationLS lsimpl = (DOMImplementationLS)domImpl.getFeature("LS", "3.0"); LSSerializer ser = lsimpl.createlsserializer(); LSOutput out = lsimpl.createlsoutput(); out.setbytestream(new FileOutputStream(args[0])); ser.write(doc, out);
Transformacje XSLT Przekształcenie dokumentów zapisanych w plikach T r a n s f o r m e r F a c t o r y t r a n s _ f a c t = T r a n s f o r m e r F a c t o r y. n e w I n s t a n c e ( ) ; t r a n s f o r m e r = t r a n s _ f a c t. newtransformer ( new StreamSource ( a r g s [ 2 ] ) ) ; Source s r c = new StreamSource ( a r g s [ 0 ] ) ; R e s u l t r e s = new S t r e a m R e s u l t ( a r g s [ 1 ] ) ; t r a n s f o r m e r. t r a n s f o r m ( s r c, r e s ) ; Transformacje Zastosowanie do zapisu zdarzeń SAX po przefiltrowaniu SAXParserFactory p a r s e r _ f a c t = SAXParserFactory. n e w I n s t a n c e ( ) ; XMLReader r e a d e r = p a r s e r _ f a c t. newsaxparser ( ). getxmlreader ( ) ; T r a n s f o r m e r F a c t o r y t r a n s _ f a c t = T r a n s f o r m e r F a c t o r y. n e w I n s t a n c e ( ) ; T r a n s f o r m e r t r a n s f o r m e r = t r a n s _ f a c t. newtransformer ( ) ; XMLFilter f i l t r = new FiltrGrupyWazne ( ) ; f i l t r. s e t P a r e n t ( r e a d e r ) ; I n p u t S o u r c e doc = new I n p u t S o u r c e ( a r g s [ 0 ] ) ; Source s r c = new SAXSource ( f i l t r, doc ) ; R e s u l t r e s = new S t r e a m R e s u l t ( a r g s [ 1 ] ) ; t r a n s f o r m e r. t r a n s f o r m ( s r c, r e s ) ;