510 likes | 795 Views
The Java Architecture For XML Binding (JAXB). By: Yoav Zibin Sharon Krisher. Motivation for JAXB. The main purpose of XML Schema is: Validation of XML documents Any other purposes? Hint 1: determinism requirement Hint 2: the “ default ” attribute has nothing to do with validation
E N D
The Java Architecture For XML Binding (JAXB) By: Yoav Zibin Sharon Krisher
Motivation for JAXB • The main purpose of XML Schema is: • Validation of XML documents • Any other purposes? • Hint 1: determinism requirement • Hint 2: the “default” attribute has nothing to do with validation <xs:attribute name="country" use=“optional” default="Israel“ /> • Answer: Given a valid XML document, its schema defines a uniquedata model
Motivation for JAXB • Problem: How to manipulate this data model? • DOM (data object model) solution: • Pros: simple, general (a schema is not even required) • Cons: no types, no compile-time checking • I wish to write … DOM pseudo-code example root.getChild("Address").getChild("Number").getText() returns a string root.getAddress().getNumber() returns a number
JAXB solution: Mapping XML Schema to Java interfaces Binding Compiler Source schema Java interfaces Pros: preserve types, compile-time checking Cons: complex, specific to a certain schema
Binding Compiler <xs:complexType name="AddressType"> <xs:sequence> <xs:element name="Number" type="xs:unsignedInt"/> <xs:element name="Street" type="xs:string"/> </xs:sequence> </xs:complexType> public interface AddressType { long getNumber(); void setNumber(long value); String getStreet(); void setStreet(String value); } Must be non-negative Must be non-null
Talk Outline • How is XML Schema mapped to Java interfaces? • What is the default mapping? • How to customize this mapping? • Second part of the lecture • How do I use those Java interfaces? • Next slides and a Demo!
Main Features • Unmarshal: xml objects • Create / Read / Update / Delete objects • Validate objects • Marshal: objects xml • No roundtrip guarantees • Marshal( Unmarshal(xml) ) ≠ xml • We found that order is not always preserved • But usually roundtrip holds
Step 1: Create XML Schema Demo.xsd <xs:element name="Person" type="PersonType"/> <xs:complexType name="PersonType"> <xs:sequence> <xs:element name=“Name" type="xs:string"/> <xs:element name="Address" type="AddressType" minOccurs="1" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> <xs:complexType name="AddressType"> <xs:sequence> <xs:element name="Number" type="xs:unsignedInt"/> <xs:element name="Street" type="xs:string"/> </xs:sequence> </xs:complexType>
Step 2: Create XML Document Demo.xml <Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:\JAXB Demo\demo.xsd"> <Name>Sharon Krisher</Name> <Address> <Street>Iben Gevirol</Street> <Number>57</Number> </Address> <Address> <Street>Moshe Sharet</Street> <Number>89</Number> </Address> </Person> Check that your XML conforms to the Schema
Step 3: Run the binding compiler • %JWSDP_HOME%\jaxb\bin\xjc -p demo demo.xsd • A package named demo is created (in the directory demo) • The package contains (among other things): • interface AddressType • interface PersonType
AddressType and PersonType public interface AddressType { long getNumber(); void setNumber(long value); String getStreet(); void setStreet(String value); } Must be non-negative Must be non-null Must be non-null public interface PersonType { String getName(); void setName(String value); /* List of AddressType */ java.util.List getAddress(); } Must contain at least one item In Java1.5: List<AddressType>
Step 4: Create Context • The context is the entry point to the API • Contains methods to create Marshaller, Unmarshaller and Validator instances JAXBContext context = JAXBContext.newInstance("demo"); The package name is demo (Recall: xjc -p demo demo.xsd)
Step 5: Unmarshal: xml -> objects Enable validation of xml according to the schema while unmarshalling Unmarshallerunmarshaller = context.createUnmarshaller(); unmarshaller.setValidating(true); PersonTypeperson = (PersonType) unmarshaller.unmarshal( new FileInputStream("demo.xml") );
Step 6: Read System.out.println("Person name=" + person.getName() ); AddressTypeaddress = (AddressType) person.getAddress().get(0); System.out.println("First Address: " + " Street=" + address.getStreet() + " Number=" + address.getNumber() );
What happens if we validate there? // Create ObjectFactoryobjectFactory = new ObjectFactory(); AddressTypenewAddr = objectFactory.createAddressType(); newAddr.setStreet("Hanoter"); newAddr.setNumber(5); addressList.add( newAddr ); part of the demo package uses the factory pattern Step 7: Manipulate objects // Update person.setName("Yoav Zibin"); // Delete ListaddressList = person.getAddress(); addressList.clear();
Step 8: Validate on-demand Validatorvalidator = context.createValidator(); validator.validate(newAddr); validator.validate(person); Check that we have set Street and Number, and that Number is non-negative Check that we have set Name, and that Address contains at least one item
Step 9: Marshal: objects -> xml Marshallermarshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE); marshaller.marshal(person, new FileOutputStream("output.xml")); output.xml <Person> <Name>Yoav Zibin</Name> <Address> <Street>Hanoter</Street> <Number>5</Number> </Address> </Person>
Similar Technologies • Liquid XML Data Binding • Similar to JAXB • Supports all Schema constructs • In addition to Java: C#, C++, Visual Basic 6 • Relaxer • Instead of Schema uses Relax • Castor.org
Second Part Outline • Validation • Mapping XML Schema to Java • Naming • Java Properties • Simple and Complex Types • Customization of the default mapping
Validation Constraints • Three categories of constraints • Type constraints: Legal values in simple types • E.g., in every address, number is a non-negative integer • Local structural constraints • E.g., in every person, address contains at least one item • Global structural constraints • E.g., ID and IDREF
Validation • Three forms of validation • Unmarshal time validation (at unmarshal time) • On-demand validation (at any chosen point in time) • validateRoot(object) vs. validate(object) • validateRoot includes global constraint checking • Fail-fast validation (at all times) • Currently not implemented • Checks that the value provided to a set method is legal • When validation errors occur an event is raised (no exception) and validation continues, so that several validation errors can be handled. • Default handler raises an exception on first error
Unsupported Schema Concepts • Substitution groups • Type substitutions (xsi:type, block) • Key, keyref, and unique • anyAttribute • No support for XPath or any other query langauge
Element vs. Type • An element also has a qualified name • When is the difference important? (next) <xs:element name=“ugly_man" type="PersonType"/> <xs:element name=“pretty_woman" type="PersonType"/> <xs:complexType name="PersonType"> … </xs:complexType> an empty interface which marks the existence of a static QName interface UglyMan extends PersonType, Element {} interface PrettyWoman extends PersonType, Element {} interface PersonType { … }
When must I use elements? • Marshal: • General content marshaller.marshal(Object, OutputStream) E.g., when we marshal a PersonType: <Name>Sharon Krisher</Name> <Address> <Street>Iben Gevirol</Street> <Number>57</Number> </Address> must be an element, otherwise the resulting output is not a legal XML Object getAny(); void setAny(Object elementOrValue); <xs:any/>
Naming • Problem: sometimes XML names • are not legal java names • do not comply to java naming standards • The binding compiler creates proper names
Java Properties • Local schema components are mapped to: • Simple property (get, set) • With customization: isSetName , unsetName • List property • Indexed property (next) String getName(); void setName(String value); In Java1.5: List<AddressType> java.util.List getAddress();
Indexed Property • Used instead of a list property when a proper customization is applied AddressType[] getAddress(); void setAddress(AddressType[] value); AddressType getAddress(int index); void setAddress(int index, AddressType value);
General Content Property • The most general content property • Can represent any content, however complex • A list that can contain element interfaces and values • Used for “problematic cases” : • Name collisions due to derivation • Mixed content • Another example: <xs:any maxOccurs="unbounded"/> Each item can be some element or value List getAny();
Simple Types (partial diagram) List Next (1) Represented as validation constraints String/Object Calendar BigInteger int Next (2)
Simple Type: Union <xs:complexType name="Date"> <xs:sequence> <xs:element name="Month"> <xs:simpleType> <xs:union memberTypes="xs:intxs:string"/> </xs:simpleType> </xs:element> </xs:sequence> </xs:complexType> Common supertype of (Integer, String) is Object Public interface Date { Object getMonth(); void setMonth(Object); }
Type Safe Enumeration <xs:simpleType name="USState"> <xs:restriction base="xs:NCName"> <xs:enumeration value="AK"/> <xs:enumeration value="NY"/> </xs:restriction> </xs:simpleType> public class USState { protected USSate(String v) {…} public static final USStateAK = …; public static final USStateNY = …; public String getValue(); public static USState fromValue(String v) {…} }
* ( ) Next XML Schema Type System Represented as a Java interface finished * * * * Elements abstract nillable minOccurs maxOccurs Attributes use default fixed * * The interfaceextends the base type’s interface Represented as Java properties
Complex Types • Represented as a Java interface • Anonymous type • An interface is created. • The name is derived from the name of the schema element + “Type”, e.g Foo FooType • Abstract types: no create method in ObjectFactory
Complex Type: Simple Content <xs:complexType name=“InternationalPrice"> <xs:simpleContent> <xs:extension base="xs:int"> <xs:attribute name="currency“ type="xs:string"/> </xs:extension> </xs:simpleContent> </xs:complexType> interface InternationalPrice { int getValue(); void setValue(int); String getCurrency(); void setCurrency(String); }
Complex Type: Aggregation <xs:element name="A" type="xs:int"/> <xs:complexType name="Foo"> <xs:sequence> <xs:element ref="A"/> <xs:sequence> <xs:element ref="B"/> <xs:element ref="C"/> </xs:sequence> </xs:sequence> </xs:complexType> <xs:complexType name="Foo"> <xs:sequence> <xs:element ref="A"/> <xs:element ref="B"/> <xs:element ref="C"/> </xs:sequence> </xs:complexType> interface Foo { int getA(); void setA(int); int getB(); void setB(int); int getC(); void setC(int); }
XML fragment Dear Mr.<name>Robert Smith</name>, … Complex Type: Mixed Content <xs:complexType name=“LetterBody" mixed="true"> <xs:sequence> <xs:element name="name" type="xs:string"/> … </xs:sequence> </xs:complexType> interface LetterBody { interface Name extends Element { String getValue(); void setValue(String); } … List getContent(); } LetterBody lb = ObjectFactory.createLetterBody(); List gcl = lb.getContent(); gcl.add("Dear Mr."); gcl.add(ObjectFactory.createLetterBodyName("Robert Smith")); The list may contain elements and strings
Complex Type: Choice <xs:complexType name="FooBarType"> <xs:choice> <xs:element name="Foo" type="xs:int"/> <xs:element name="Bar" type="xs:string"/> </xs:choice> </xs:complexType> The programmer is responsible to only set one of Foo or Bar Default public interface FooBarType { int getFoo(); void setFoo(int value); String getBar(); void setBar(String value); boolean isSetFoo(); void unsetFoo(); boolean isSetBar(); void unsetBar(); } Customization (Not implemented yet) public interface FooBarType { Object getFooOrBar(); void setFooOrBar(Object); } Common supertype of (Integer, String) is Object Similar to union
When maxOccurs>1 <xs:complexType name="FooBarType"> <xs:choicemaxOccurs="unbounded"> <xs:element name="Foo" type="xs:int"/> <xs:element name="Bar" type="xs:string"/> </xs:choice> </xs:complexType> public interface FooBarType { interface Foo extends Element {…} interface Bar extends Element {…} // Items are instances of Foo and Bar List getFooOrBar(); } For asequence: List getFooAndBar() The programmer needs to make sure thatthe list contains sequences of <A,B>.
Objects in memory XML XML ≠ Complex Type: All • Mapped like Sequence • Can’t have maxOccurs > 1 • No way to specify the order of elements • Round trip doesn’t hold (order is not preserved): unmarshal marshal
Java: or setAge( new Integer(25) ) setAge(null) XML: or <age>25</age> <agexsi:nil="true"/> Nillable elements <xs:element name=“age" type=“xs:int" nillable="true"/> Integer getAge(); void setAge(Integer value);
Customization • Used to augment the default mapping • Customizations declared via special xml tags • May appear inside the source schema or in a separate file
Uses of Customization • Change default names of interfaces and properties • For a specific schema construct • For an entire namespace • Add a suffix or a prefix • Change the types of properties • Add javadoc declarations
Software Engineering Issues • Weak Typing Our suggestions: • Marshal / Unmarshal : Object Element • IDREF: Object Identifiable • Sometimes JAXB doesn’t allow us to control the order of elements • Example 1: xs:all • Example 2: next slide
Element Order Example <xs:complexType name=“ABType"> <xs:choice> <xs:sequence> <xs:element name="A" type="xs:int"/> <xs:element name="B" type="xs:string"/> </xs:sequence> <xs:sequence> <xs:element name="B" type="xs:string"/> <xs:element name="A" type="xs:int"/> </xs:sequence> </xs:choice> </xs:complexType> obj.setA(5); obj.setB(“a”); What happens when we marshal obj? public interface ABType { int getA(); void setA(int value); String getB(); void setB(String value); } No roundtrip
xs:choice between Foo and Bar obj.setFoo(42); obj.setBar("A"); System.out.println( obj.isSetFoo() ); It’s the programmer’s fault • Taken from JAXB specification: • “The caller must be sure …” • “There is an expectation …” • “User is responsible …” • “… unexpected behavior may occur.” Question: What is the output ? 42 true obj.setAge(42); obj.unsetAge(); System.out.println( obj.getAge() );
The END • Any questions?