티스토리 뷰
[참조-한글] http://blog.naver.com/mirnae?Redirect=Log&logNo=100054958660
http://blogs.sun.com/CoreJavaTechTips/entry/exchanging_data_with_xml_and
Exchanging Data with XML and JAXB, Part 1
by Jennie Hall
In this tip, you'll learn how to to use the Java Architecture for XML Binding (JAXB) in Java SE 6to exchange XML data between systems without having to delve into thespecifics of XML processing. Among other key improvements, JAXB 2.0offers support for binding Java objects to XML via the use ofannotations. This feature allows you to generate XML data from yourapplication simply by annotating your existing object model.
What's JAXB?
JAXB simplifies the use of XML data in Java applications by shieldingyou and your code from the low-level details of working with XML.Essentially, JAXB allows you to move easily back-and-forth between XMLand Java. A JAXB implementation supplies a schema compiler, which takesin an XML schema and generates Java classes which map to that schema.Data from XML documents which are instances of the schema can be boundautomatically to the generated classes by JAXB's binding runtimeframework. This is called unmarshalling. Once unmarshalled, content canbe manipulated or modified in Java as needed. JAXB can also write(marshal) data from Java objects to an XML instance document. JAXBoptionally performs validation of content as part of these operations.
Inaddition to the schema compiler, a JAXB implementation also provides aschema generator, which looks at annotations on Java classes andgenerates a corresponding XML schema. This feature is new to JAXB 2.0.
The Sample Application
A veterinary office wants to send notices to its clients reminding themof their pets' upcoming appointments. Because they are nice folks, theyalso like to send a birthday card to an owner on the pet's birthday.The veterinary office, NiceVet, contracts with a service provider,WePrintStuff, to do their printing and mailing for them.
NiceVet already has an application which maintainsinformation about the animals under NiceVet's care and their owners.Here's a diagram of NiceVet's existing object model:
NiceVetneeds a way to get the necessary notification data to WePrintStuff forprocessing without spending a lot of time and money modifying theircurrent application.
Generating the XML
It'stime
to annotate our object model so we can generate some XML data forthe
printing and mailing service. Looking at NiceVet's object model, wesee
that the PrintOrder
class holds the information
aboutupcoming events, and thus makes sense as the root element of the
datawe want to send. Let's add the @XmlRootElement
annotation to the PrintOrder
class. This will establish printOrder
as the root element of our generated XML.
// the root element of our generated XML
@XmlRootElement(name = "printOrder")
public class PrintOrder {
private long id;
private List<Event> events;</pre>
Let's add some more annotations:
<pre> import javax.xml.bind.annotation.XmlRootElement;import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
// the root element of our generated XML
@XmlRootElement(name = "printOrder")
// maps this class to a generated schema type
@XmlType(name = "PrintOrderType", propOrder = {"events"})
// bind all non-static, non-transient fields
// to XML unless annotated with @XmlTransient
@XmlAccessorType(XmlAccessType.FIELD)
public class PrintOrder {
// maps this field to an XML attribute
@XmlAttribute(required = true)
private long id;
// custom adapter maps this field to a type,
// NotificationsType, which makes it easy to
// generate the desired XML
@XmlJavaTypeAdapter(value=EventAdapter.class)
@XmlElement(name = "notifications")
private List<Event> events;</pre>
@XmlType
maps the PrintOrder
class to a generated schema type named PrintOrderType
. The @XmlAccessorType(XmlAccessType.FIELD)
annotation tells JAXB to bind all non-static, non-transient fields to XML unless annotated with @XmlTransient
, which prevents the mapping of a field or property. With the XmlAccessType.FIELD
setting, getter/setter pairs are not bound to XML unless explicitly annotated. The default setting is XmlAccessType.PUBLIC_MEMBER
(all public fields and getter/setter pairs are bound unless otherwise annotated); other settings are XmlAccessType.PROPERTY
and XmlAccessType.NONE
.
Moving down the code listing, we see that the field id maps to an XML attribute on the PrintOrderType
element. Some kind of identifier on the print order is required, and we've annotated the id field accordingly.
The PrintOrder
class has a list of events which contains both appointments and birthdays. The annotation @XmlJavaTypeAdapter
tells JAXB that we are going to use a custom adapter, EventAdapter
, to help us map this Java construct to XML. To support this annotation, we need to write an adapter class which extends XmlAdapter
and overrides its abstract marshal and unmarshal methods.
// adapt original Java construct to a type, NotificationsType,
// which we can easily map to the XML output we want
public NotificationsType marshal(List<Event> events) throws Exception {
List<Appointment> appointments = new ArrayList<Appointment>();
List<Birthday> birthdays = new ArrayList<Birthday>();
for (Event e : events) {
if (e instanceof Appointment) {
appointments.add((Appointment)e);
} else {
birthdays.add((Birthday)e);
}
}
return new NotificationsType(appointments, birthdays);
}
// reverse operation: map XML type to Java
public List<Event> unmarshal(NotificationsType notifications) throws Exception { ... }
</pre>
EventAdapter's marshal method takes the original Java construct, List<Event>
events, and converts it to a type which maps more easily to the XML output we want. Let's take a look at NotificationsType
:
import javax.xml.bind.annotation.XmlElement;
import java.util.List;
public class NotificationsType {
// produce a wrapper XML element around this collection
@XmlElementWrapper(name = "appointments")
// maps each member of this list to an XML element named appointment
@XmlElement(name = "appointment")
//private List<Appointment> appointments;
private List<Appointment> appointments;
// produce a wrapper XML element around this collection
@XmlElementWrapper(name = "birthdays")
// maps each member of this list to an XML element named birthday
@XmlElement(name = "birthday")
//private List<Birthday> birthdays;
private List<Birthday> birthdays;
public NotificationsType() {}
public NotificationsType(List<Appointment> appointments, List<Birthday> birthdays) {
this.appointments = appointments;
this.birthdays = birthdays;
}
</pre>
The @XmlElementWrapper
annotation creates a wrapper XML element around each collection, and the @XmlElement
annotation maps each member of the collection to an XML element of the appropriate type. Combined with the @XmlElement(name = "notifications")
annotation on PrintOrder.events
, what we end up with will look something like this:
<notifications>
<appointments>
<appointment>
...
</appointment>
</appointments>
<birthdays>
<birthday>
...
</birthday>
</birthdays>
</notifications>
</printOrder>
</pre>
Another
annotation worth mentioning lies in the abstractbase class Event
(listing below), from which subclasses Appointment andBirthday inherit.
This inheritance hierarchy won't hold much meaningfor our service
provider, WePrintStuff, so we'll eliminate it with the @XmlTransient
annotation on Event
. We still need fields owner and pet, however, so we annotate them with @XmlElement(required = true)
.
import javax.xml.bind.annotation.XmlTransient;
// no need to capture this inheritance hierarchy in XML
@XmlTransient
public abstract class Event {
@XmlElement(required = true)
private Owner owner;
@XmlElement(required = true)
private Pet pet;
public Event() {}
public Event(Owner owner, Pet pet) {
this.owner = owner;
this.pet = pet;
}
</pre>
When
objects of type Birthday and Appointment are marshalled to XML,they
will include all the relevant data from their supertype, Event
. Take a look at the listing below for the Birthday
class:
@XmlAccessorType(XmlAccessType.FIELD)
public class Birthday extends Event {
@XmlElement(required = true)
private int age;
public Birthday() {}
public Birthday(Owner owner, Pet pet) {
super(owner, pet);
this.age = this.calculateAge();
}
@XmlElement(name = "birthday", required = true)
public Date getBirthday() {
return this.getPet().getDateOfBirth();
}
</pre>
@XmlType.propOrder
specifies the order inwhich content is marshalled and unmarshalled.
This order wouldotherwise be unspecified due to the nature of Java
reflection.Alternatively, you can impose an order with the @XmlAccessorOrder
annotation. As mentioned in the Java Tutorial, imposing an order improves application portability across JAXB implementations.
Although the field dateOfBirth
is actually on the Pet
class,
it makes more sense in terms of our XML representation if thisdata is
organized as an element under the birthday element. We canachieve this
without a dateOfBirth
field on the Birthday
class by annotating the getBirthday()
method, which delegates to Pet
.
Ona
related note, WePrintStuff doesn't need to do any additionalprocessing
with the dates NiceVet sends them; they just need to locatethe correct
notification template and print the information out. As wesaw earlier
with the events list on the PrintOrder
class,we can
supply a custom adapter to get the desired XML output. Thistime,
however, we'll specify the adapter through a package-levelannotation.
The adapter will take in all java.util.Date
instances and output nicely formatted Strings
. The package-level annotation goes in the package-info.java
file (located in the vet package):
// processed by DateAdapter
@XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class)
package vet;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.Date;
</pre>
AlthoughJAXB can handle marshalling java.util.Date to XML in a default manneron its own, we're telling JAXB to use an alternative, customizedmapping which better suits our purposes. We implement the adapter inmuch the same way as above:
<pre> import java.util.Date;import java.text.SimpleDateFormat;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DateAdapter extends XmlAdapter<String, Date> {
// the desired format
private String pattern = "MM/dd/yyyy";
public String marshal(Date date) throws Exception {
return new SimpleDateFormat(pattern).format(date);
}
public Date unmarshal(String dateString) throws Exception {
return new SimpleDateFormat(pattern).parse(dateString);
}
</pre>
Theannotations
on the other classes in the object model are similar towhat we've seen
so far; take a peek at the sample code included withthis tip to see
exactly what's going on in the other classes.
We've finished
annotating NiceVet's object model and we're ready togenerate some XML.
If we run the sample application, we should see anXML instance document
something like the following listing. Thedocument, niceVet.xml, is
located in the root directory of thejava-to-schema project.
<printOrder id="1218226109781">
<notifications>
<appointments>
<appointment>
<apptType>Yearly Checkup</apptType>
<apptDate>09/15/2008</apptDate>
<owner>
<firstName>Joe</firstName>
<lastName>Outdoors</lastName>
<address>
<addressLine1>123 Whitewater Street</addressLine1>
<city>OurTown</city>
<state>CA</state>
<zip>90347</zip>
<zipExt>1234</zipExt>
</address>
</owner>
<pet>
<name>Honcho</name>
<species>Dog</species>
</pet>
</appointment>
<appointment>
<apptType>Well Mom Exam</apptType>
<apptDate>09/12/2008</apptDate>
<owner>
<firstName>Missy</firstName>
<lastName>Fairchild</lastName>
<address>
<addressLine1>456 Scenic Drive</addressLine1>
<city>West OurTown</city>
<state>CA</state>
<zip>90349</zip>
<zipExt>6789</zipExt>
</address>
</owner>
<pet>
<name>Miss Kitty</name>
<species>Cat</species>
</pet>
</appointment>
</appointments>
<birthdays>
<birthday>
<age>7</age>
<birthday>09/07/2000</birthday>
<owner>
<firstName>Violet</firstName>
<lastName>Flowers</lastName>
<address>
<addressLine1>22375 Willow Court</addressLine1>
<city>West OurTown</city>
<state>CA</state>
<zip>90349</zip>
<zipExt>6789</zipExt>
</address>
</owner>
<pet>
<name>Tom</name>
<species>Cat</species>
</pet>
</birthday>
</birthdays>
</notifications>
</printOrder>
</pre>
The generated schema for the XML instance document above is named schema1.xsd
, and it lives in /generated/schema
under the root directory of the java-to-schema project. It looks like this:
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="printOrder" type="PrintOrderType"/>
<xs:complexType name="PrintOrderType">
<xs:sequence>
<xs:element name="notifications" type="notificationsType" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="id" type="xs:long" use="required"/>
</xs:complexType>
<xs:complexType name="notificationsType">
<xs:sequence>
<xs:element name="appointments" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="appointment" type="AppointmentType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="birthdays" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="birthday" type="BirthdayType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AppointmentType">
<xs:sequence>
<xs:element name="apptType" type="xs:string"/>
<xs:element name="apptDate" type="xs:string"/>
<xs:element name="owner" type="OwnerType"/>
<xs:element name="pet" type="PetType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="OwnerType">
<xs:sequence>
<xs:element name="firstName" type="xs:string"/>
<xs:element name="lastName" type="xs:string"/>
<xs:element name="address" type="AddressType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AddressType">
<xs:sequence>
<xs:element name="addressLine1" type="xs:string"/>
<xs:element name="addressLine2" type="xs:string" minOccurs="0"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="state" type="xs:string"/>
<xs:element name="zip" type="xs:string"/>
<xs:element name="zipExt" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="PetType">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="species" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="BirthdayType">
<xs:sequence>
<xs:element name="age" type="xs:int"/>
<xs:element name="birthday" type="xs:string"/>
<xs:element name="owner" type="OwnerType"/>
<xs:element name="pet" type="PetType"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</pre>
If we take a quick peek at the main()
method, we can see that we are generating the schema as follows:
final File dir = new File("generated" + File.separator + "schema");
// specify a name for the generated XML instance document
OutputStream os = new FileOutputStream("niceVet.xml");
// create a JAXBContext for the PrintOrder class
JAXBContext ctx = JAXBContext.newInstance(PrintOrder.class);
// generate an XML schema from the annotated object model; create it
// in the dir specified earlier under the default name, schema1.xsd
ctx.generateSchema(new SchemaOutputResolver() {
@Override
public Result createOutput(String namespaceUri, String schemaName) throws IOException {
return new StreamResult(new File(dir, schemaName));
}
});
</pre>
To get the XML instance document, we first create some data, then we obtain a Marshaller from the JAXBContext
and marshal the Java content tree to XML:
PrintOrder order = new PrintOrder(EventUtil.getEvents());
...
// create a marshaller
Marshaller marshaller = ctx.createMarshaller();
// the property JAXB_FORMATTED_OUTPUT specifies whether or not the
// marshalled XML data is formatted with linefeeds and indentation
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// marshal the data in the Java content tree
// to the XML instance document niceVet.xml
marshaller.marshal(order, os);
</pre>
Running the Sample Application
To run the sample application, downloadthe
sample code and unzip it. Launch NetBeans and select File ->Open
Project. In the Open Project dialog box, navigate to the directorywhere
you unzipped the sample code and select the folderjava-to-schema.
Select the Open as Main Project check box. Click OpenProject Folder.
Right-click the java-to-schema project and select RunProject.
Conclusion
In this tip, you've seen howJAXB makes it easy to generate XML data to exchange with a businesspartner. The XML data your partner receives, however, may need a littletweaking in order to mesh with their system. In a future tip, you'lllearn how to apply customizations to a supplied schema to get the Javaobject model - and content tree - you want.
References and Resources
Jennie Hall is a lead developer working in the financial sector.
'Computer > XML' 카테고리의 다른 글
[JAXB] 실제 활용하기. (1) | 2009.12.17 |
---|---|
[JAXB] XML Binding을 위한 JAXB 참고 URL (0) | 2009.11.11 |
- Total
- Today
- Yesterday
- VirtureBox
- 페밀리세일
- 새희망씨앗
- 팸세일
- 나이지리아
- javascript
- 기부사기
- 새희망씨앗 피해자
- 해외직구
- 와코마리아한남동
- 홈택스
- eclipse
- 기부피해
- java
- 리터너블
- UML
- 와코마리아 스웨터
- 와코마리아
- 사업자등록
- 라고스
- 와코마리아팸세일
- 단체소송
- 6월 패밀리세일
- MySQL
- 한남동세일
- 마라탕 #마라상궈 #마라탕재료 #쉽게만드는마라탕 #중딩입맛 #마라탕잡탕
- 패밀리세일
- 안드로이드
- 인도
- 부드러운맛
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |