I'm using JAXB to unmarshal some xml into an object(s).
I have a class which inherit from an abstract class. I've marked the abstract class as #XmlTransient. Then using XMLType PropOrder I can access the properties in the abstract class like so:
#XmlType( propOrder = { "id"...
Cool. Problem is sometimes it isn't an element that I want to access but rather an attribute. Normally you would define such a property using #XMLAttribute to indicate the value is stored in an xml attribute and not an element. But given the fact that I've already used XMLTransient on the abstract class where 'id' is defined, JAXB complains when I try to mark the field as #XMLAttribute.
JAXB is complaining that I'm trying to access/return two fields of with the same name.
Can anyone please point me in the right direction? I'm building for GAE so I dn't really want to use any other libraries.
Thanks in advance!
Below are a couple of things you can do:
Java Model
Foo
You can annotate the property on the parent class with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlTransient
public class Foo {
private String att1;
private String att2;
#XmlAttribute
public String getAtt1() {
return att1;
}
public void setAtt1(String att1) {
this.att1 = att1;
}
public String getAtt2() {
return att2;
}
public void setAtt2(String att2) {
this.att2 = att2;
}
}
Bar
You can override the property on the subclass and annotate it with #XmlAttribute.
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Bar extends Foo {
#Override
#XmlAttribute
public String getAtt2() {
return super.getAtt2();
}
#Override
public void setAtt2(String att2) {
super.setAtt2(att2);
}
}
Demo Code
Demo
Here is some demo code you can run to show that everything works.
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Bar.class);
Bar bar = new Bar();
bar.setAtt1("a");
bar.setAtt2("b");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(bar, System.out);
}
}
Output
Below is the output from running the demo code:
<?xml version="1.0" encoding="UTF-8"?>
<bar att1="a" att2="b"/>
Related
Is there a way with jaxb to create a custom element from two fields from an object?
Here's an example of what I have and what I want. I realize I could extract the fields into a seperate annotated class, but I'm curious of theres a way to do something similar to this.
#XmlRootElement()
public class Foo {
public String bar
public String baz
}
Expected output xml:
<foo>
<customElement bar="barValue">bazValue</customElement>
</foo>
Thanks!
class for foo
#XmlRootElement()
public class Foo {
private customElement CustomElement;
public CustomElement getCustomElement(){
return customElement;
}
#XmlElement
public void setCustomElement(CustomElement customElement){
this.customElement = customElement;
}
}
class for custom element
#XmlAccessorType(XmlAccessType.FIELD)
public class CustomElement {
#XmlAttribute
private String bar;
#XmlValue
private String baz
// set getters and setters
}
I have 2 classes, one extends the other. The superclass marshals correctly, but the subclass, which adds one attribute, does not. The extra attribute is not present in the XML.
Superclass:
#XmlRootElement()
#XmlAccessorType(XmlAccessType.NONE)
public class SessionRecord extends Record {
SimpleDateFormat hhmm = new SimpleDateFormat("HH:mm");
SimpleDateFormat day = new SimpleDateFormat("EEEEE");
#XmlAttribute protected int sessionId;
#XmlAttribute protected boolean open;
#XmlAttribute protected boolean night;
protected Date start;
protected Date finish;
protected boolean setup;
protected boolean takedown;
#XmlAttribute
public String getDescription() {
if (start==null) start = new Date();
if (finish==null) finish = new Date();
return day.format(start)+(night ? " Night " : " ")+hhmm.format(start)+"-"+hhmm.format(finish)+" "+type();
}
private String type() {
return setup ? "Setup" : (open ? "Open" : (takedown ? "Takedown" : ""));
}
#XmlAttribute
public boolean isSetupTakedown() {
return setup || takedown;
}
}
This produces XML elements similar to this:
<sessionRecord setupTakedown="true" description="Saturday 09:00-13:00 Setup" night="false" open="false" sessionId="0"/>
which is OK.
But the subclass:
#XmlRootElement()
public class VolunteerSession extends SessionRecord {
#XmlAttribute private boolean available;
}
Produces identical output, the available attribute is not marshalled. Why is JAXB not marshalling the extra attribute?
EDIT
further information:
Record superclass is merely this:
public abstract class Record {}
Here is the class representing the top-level document element. It contains lists of Records:
#XmlRootElement(name="response")
#XmlSeeAlso({
RecordList.class,
VolunteerAssignment.class,
VolunteerRecord.class,
SessionRecord.class,
VolunteerSession.class,
VolunteerArea.class,
PossibleAssignment.class})
public class XMLResponse {
#XmlAttribute private String errorMessage;
private List<RecordList<? extends Record>> recordLists = new ArrayList<RecordList<? extends Record>>();
//snip...
public void setError(String errorMessage) {
this.errorMessage = errorMessage;
}
#XmlMixed
public List<RecordList<? extends Record>> getRecordLists() {
return recordLists;
}
}
and finally, RecordList
#XmlRootElement()
public class RecordList<T extends Record> {
#XmlAttribute private String name;
#XmlAttribute private int total;
#XmlAttribute private int count;
#XmlAttribute private int start;
#XmlAttribute private boolean update;
private List<T> records;
// snip constructors, setters
#XmlMixed
public List<T> getRecords() {
return records;
}
}
It sounds as though the VolunteerSession class is not being included in the JAXBContext. This can happen depending on how you created your JAXBContext. Below is some example code where the same object is marshalled based on 3 different instances of JAXBContext each bootstrapped off a different class.
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
VolunteerSession volunteerSession = new VolunteerSession();
marshal(VolunteerSession.class, volunteerSession);
marshal(SessionRecord.class, volunteerSession);
marshal(XMLResponse.class, volunteerSession);
}
private static void marshal(Class bootstrapClass, Object object) throws Exception {
System.out.println(bootstrapClass.getName());
JAXBContext jc = JAXBContext.newInstance(bootstrapClass);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, System.out);
System.out.println();
}
}
Output
When the JAXBContext is bootstrapped off of VolunteerSession obviously it has the necessary information.
When the JAXBContext is bootstraped off of the super class SessionRecord it doesn't pull in VolunteerSession. JAXB will automatically process metadata for super classes, but not subclasses. #XmlSeeAlso is usually used in this case to reference mapped subclasses.
VolunteerRecord contains an #XmlSeeAlso annotation that references VolunteerSession. Therefore VolunteerSession is processed as part of the JAXBContext and contains the necessary information when marshalled.
forum20908213.VolunteerSession
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<volunteerSession available="false" sessionId="0" open="false" night="false" description="Sunday 05:53-05:53 " setupTakedown="false"/>
forum20908213.SessionRecord
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sessionRecord sessionId="0" open="false" night="false" description="Sunday 05:53-05:53 " setupTakedown="false"/>
forum20908213.XMLResponse
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<volunteerSession available="false" sessionId="0" open="false" night="false" description="Sunday 05:53-05:53 " setupTakedown="false"/>
You have to list all of your subclasses in #XmlSeeAlso annotation of your parent class.
I'm trying to implement all Java classes for handling the following XML code snippet:
<party date="2012-09-30">
<guest name="Albert">
<drink>wine</drink>
</guest>
</party>
I've wrote 3 classes:
Party.java:
package li.mnet.www.java.xml;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "party")
public class Party {
#XmlAttribute(name = "date")
private String partyDate;
public Party() {}
public String getPartyDate() {return partyDate;}
public void setPartyDate(String partyDate) {
this.partyDate = partyDate;
}
}
Guest.java:
package li.mnet.www.java.xml;
import javax.xml.bind.annotation.XmlElement;
public class Guests {
private String name;
public Guests() {}
public void setGuestName(String name) {this.name = name;}
#XmlElement(name = "name")
public String getGuestName() {return name;}
}
PartyRunner.java:
package li.mnet.www.java.xml;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class PartyRunner {
public static void main(String[] args) {
Party party = new Party();
Guests guest = new Guests();
party.setPartyDate("2012-09-03");
guest.setGuestName("Albert");
JAXBContext context;
try {
context = JAXBContext.newInstance(Party.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(party, System.out);
} catch (JAXBException e) {e.printStackTrace();}
}
}
After running the application i get following console output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<party date="2012-09-03"/>
What do i have to change, that class Guest.java gets printed out too?
Thanks a lot for your support!
When you're trying to marshal some data through JAXB, you'll give it an instance of your class (here is Party) and it will traverse your object and all of its attributes and tries to serialize them to the final output using hints provided by JAXB annotations.
Remember that JAXB ignores properties of the class which has no JAXB annotation. Also You can it tell whether to generate an XML Element or XML Attribute for given class property. You can use these annotations on properties or their getter methods.
In your example, you want to have a guest element inside party element. In your main method (in PartyRunner), you're marshaling an instance of Party class, but this class has no reference to your Guests class. Creating an instance of Guests class wouldn't be enough. You have to create a logical relationship between two classes and annotate them to make it possible to generate an appropriate XML. So your Party class should be something like this:
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "party")
public class Party {
#XmlAttribute(name = "date")
private String partyDate;
#XmlElement(name="guest")
private Guests guests;
public Party() {}
public String getPartyDate() {return partyDate;}
public void setPartyDate(String partyDate) {
this.partyDate = partyDate;
}
public Guests getGuests() {
return guests;
}
public void setGuests(Guests guests) {
this.guests = guests;
}
}
If you run the PartyRunner again you'll have something like this in your output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<party>
<partyDate>2012-09-03</partyDate>
<guest>
<name>Albert</name>
</guest>
</party>
As you can see, there is an inner element named name in your guests element. This is due to the annotation you've specified for getGuestName method in your Guests class. In order to make JAXB to generate an XML attribute for this property (instead of an XML element), you need to change the JAXB annotation in your Guests class too. You have annotated the getGuestName method as #XmlElement. This will cause to generate an XML element. If you change it to #XmlAttribute and run the PartyRunner again, you'll have this in your output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<party date="2012-09-03">
<guest name="Albert"/>
</party>
Also in your sample XML you have a drink element inside your guests property. Same is true for drink property. The drink could be a String property in your Guests class annotated as #XmlAttribute(name = "drink"). So your final Guests class to generate the XML mentioned at the beginning of your question should be something like this:
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
public class Guests {
private String name;
private String drinkType;
public Guests(){}
public void setGuestName(String name) {this.name = name;}
#XmlAttribute(name = "name")
public String getGuestName() {return name;}
#XmlElement(name = "drink")
public String getDrinkType() {
return drinkType;
}
public void setDrinkType(String drinkType) {
this.drinkType = drinkType;
}
}
and your PartyRunner should initialize the drink property to something like wine:
public static void main(String[] args) {
Party party = new Party();
Guests guest = new Guests();
party.setPartyDate("2012-09-03");
guest.setGuestName("Albert");
guest.setDrinkType("wine");
party.setGuests(guest);
JAXBContext context;
try {
context = JAXBContext.newInstance(Party.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(party, System.out);
} catch (JAXBException e) {e.printStackTrace();}
}
I think the party class should contain a guest (or list of guests if you want more)
public class Party {
#XmlAttribute(name = "date")
private String partyDate;
private Guest guest;
public Party() {}
public String getPartyDate() {return partyDate;}
public void setPartyDate(String partyDate) {
this.partyDate = partyDate;
}
// getter and setter for guest
}
set the guest of the party object then marshal the party object.
Also change the XmlElement annotiantion to XmlAttribute in the guest class at the name.
Consider the following abstract class -
public abstract class Car
{
public abstract void drive(double miles);
}
Here's a sample class (for illustration purposes) that extends the above class.
public class Ferrari extends Car
{
private String lastUsed; // Ferrari specific field not in Car
private boolean f1Car; // Ferrari specific field not in Car
#XmlElement
public void setF1Car(boolean f1Car)
{
this.f1Car = f1Car;
}
public boolean isF1Car() { return f1Car; }
#XmlElement
public void setLastUsed(String lastUsed)
{
this.lastUsed = lastUsed;
}
public String getLastUsed() { return lastUsed; }
public void drive(double miles)
{
// implementation
}
}
I have a report class that contains a Car object -
#XmlRootElement
public class CarTestReport
{
private String date;
private double miles;
private Car car;
#XmlElement
public void setDate(String date) { this.date = date;}
public String getDate() {return date;}
#XmlElement
public void setMiles(double miles) { this.miles = miles; }
public double getMiles() {return miles;}
#XmlElement
public void setCar(Car car) { this.car = car; }
public Car getCar() { return car; }
}
And here is the piece of code using JAXB to Marshall a CarTestReport object -
public static void main(String[] args) throws Exception
{
Ferrari ferrari = new Ferrari();
ferrari.setLastUsed("July 5 2012");
ferrari.setF1Car(false);
CarTestReport report = new CarTestReport();
report.setDate("July 6 2012");
report.setMiles(200);
report.setCar(ferrari);
File file = new File("carTestReport.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(CarTestReport.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(report, file);
}
The problem is, because of the abstract type Car, JAXB ignores it and doesn't marshall the Ferrari object when it marshals the CarTestReport object. The output I get is this -
<carTestReport>
<car/>
<date>July 6 2012</date>
<miles>200.0</miles>
</carTestReport>
As you can see, nothing was entered under the 'car' node, even though the Ferrari object was populated. How to solve this problem?
The JAXB system doesn't look through the classpath for any possible JAXB-annotated classes. You have to help it find them. In your sample code, it simply doesn't know about the existence of the Ferrari class. (It only sees Car because that's the return type of the getter in CarTestReport.)
One quick and dirty way to tell JAXB about Ferrari is to add #XmlSeeAlso({Ferrari.class}) at the top of your Car class. Then you'll get output like this:
<carTestReport>
<car xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ferrari">
<f1Car>false</f1Car>
<lastUsed>July 5 2012</lastUsed>
</car>
<date>July 6 2012</date>
<miles>200.0</miles>
</carTestReport>
Another way to tell JAXB about Ferrari would be to pass that class to the JAXBContext.newInstance method, i.e.:
JAXBContext jaxbContext = JAXBContext.newInstance(CarTestReport.class,
Ferrari.class);
Or if all of your JAXB classes are in the same package, e.g. com.mycompany.carstuff, then you could do this:
JAXBContext jaxbContext = JAXBContext.newInstance("com.mycompany.carstuff");
And in this last case it WILL search for all classes in that package.
If you want it to emit an element named ferrari (instead of the <car xsi:type="ferrari"> like above), one possibility is to add #XmlType to the top of your Car class, like this:
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
#XmlSeeAlso({Ferrari.class})
#XmlType
public abstract class Car {
public abstract void drive(double miles);
}
...and put #XmlRootElement on Ferrari, e.g.:
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Ferrari extends Car {
// ...
}
From what I understand, this combination of annotations tells JAXB that the Car class maps to an XML schema type (so you won't get any elements named "car"), and that the Ferrari class is an element of that type (so you can have elements named "ferrari"). And the "root" in #XmlRootElement is misleading... it can be an element anywhere in the structure of your objects.
You need to annotate your Ferrari class with #XmlRootElement and replace #XmlElement on the car attribute with #XmlElementRef
I'm using java jaxb2.0 to do xml marshalling and unmarshalling.
I encountered a problem like this.
the sample xml is like this*(fruit.xml)*:
<fruitPacks>
<fruit name="apple1" type="apple" isApple="true"/>
<fruit name="banana1" type="banana" isBanana="true"/>
<fruit name="apple2" type="apple" isApple="true"/>
</fruitPacks>
and java class like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "fruitPacks")
#XmlRootElement
public class FruitPacks
{
#XmlElement(name = "fruit")
private List<Fruit> fruits
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "fruit")
#XmlSeeAlso({ Apple.class, Banana.class })
public class Fruit{
#XmlAttribute
private String name;
#XmlAttribute
private String type;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "apple")
public class Apple extends Fruit{
#XmlAttribute
private boolean isApple = true;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "banana")
public class Banana extends Fruit{
#XmlAttribute
private boolean isBanana = true;
}
and unmarshal code is :
public class JAXBTest {
public static void main(String [] args) throws Exception{
JAXBContext jc = JAXBContext.newInstance(FruitPacks.class,Fruit.class,Apple.class, Banana.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Object obj = unmarshaller.unmarshal(new File("fruit.xml"));
System.out.println(obj);
}
}
So what I want to do is:
when unmarshalling the xml, the construction of the fruit will automatically done to the subclasses (Apple, Banana) based on the type attribute.
How can i do it?
You need to ensure that your JAXBContext is aware of the subclasses. One way to do this is to use the #XmlSeeAlso annotation, see an example below:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "fruit")
#XmlSeeAlso({Apple.class, Banana.class})
public class Fruit{
#XmlAttribute
private String name;
#XmlAttribute
private String type;
}
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
UPDATE
I just realized you are using the type attribute and not the standard xsi:type attribute. With any JAXB implementation you could map this use case leveraging an XmlAdapter:
http://blog.bdoughan.com/2012/01/jaxb-and-inhertiance-using-xmladapter.html
If you are using EclipseLink JAXB (MOXy) then you can leverage the #XmlDescriminatorNode/#XmlDescriminatorValue extension:
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-moxy-extension.html
#Gang try you get recursive code and with id attributes in the tag XML. :)