I was trying to figure out if it is possible to unmarshall an xml element to multiple pojos. for example:
for xml:
<type>
<id>1</id>
<cost>12</cost>
<height>15</height>
<width>13</width>
<depth>77</depth>
</type>
Item class
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement(name="type")
public class Item {
private Integer id;
private Double cost;
#XmlElement(name="id")
public Integer getId(){
return id;
}
#XmlElement(name="cost")
public Double getCost(){
return cost
}
}
ItemDimensions Class
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement(name="type")
public class ItemDimensions {
private Integer height;
private Integer width;
private Integer depth;
#XmlElement(name="height")
public Integer getHeight(){
return height;
}
#XmlElement(name="width")
public Integer getWidth(){
return width;
}
#XmlElement(name="depth")
public Integer getDepth(){
return depth;
}
}
I have tried to accomplish something similar using a number of JAXB mappings generated by Netbeans 6.9 and a number of test classes but have gotten nowhwere. Does anyone know if this is something that can be done without any intermediary objects?
You could use the #XmlPath extension in EclipseLink JAXB (MOXy) to accomplish this use case (I'm the MOXy tech lead):
Root
JAXB requires a single object to unmarshal, we will introduce a class to fulfill this role. This class will have fields corresponding to the two Objects you wish to unmarshal annotated with the self XPath: #XmlPath(".")
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="type")
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlPath(".")
private Item item;
#XmlPath(".")
private ItemDimensions itemDimensions;
}
ItemDimensions
You annotate this class normally. In your example you annotate the properties, but only provide getters. This will cause JAXB to think that those are write only mappings.
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class ItemDimensions {
private Integer height;
private Integer width;
private Integer depth;
}
Item
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private Integer id;
private Double cost;
}
Demo
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller u = jc.createUnmarshaller();
Object o = u.unmarshal(new File("input.xml"));
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(o, System.out);
}
}
jaxb.properties
To use MOXy as your JAXB implementation, you must provide a file named jaxb.properties in with your domain objects with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Related
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.
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"/>
I'm working on a tool to query Eve-Central. Eve-Central queries are returned in XML format, as such:
<evec_api version="2.0" method="marketstat_xml">
<marketstat>
<type id="608">
<buy>
<volume>74018</volume>
<avg>68274.08</avg>
<max>410000.01</max>
<min>25000.23</min>
<stddev>97055.39</stddev>
<median>50000.00</median>
<percentile>255537.71</percentile>
</buy>
<sell>
<volume>15324</volume>
<avg>477255.37</avg>
<max>1914490.39</max>
<min>175000.00</min>
<stddev>266422.73</stddev>
<median>407994.99</median>
<percentile>309282.09</percentile>
</sell>
<all>
<volume>87592</volume>
<avg>107228.56</avg>
<max>486000.00</max>
<min>10.00</min>
<stddev>123725.64</stddev>
<median>50000.00</median>
<percentile>47814.35</percentile>
</all>
</type>
</marketstat>
</evec_api>
I would like to parse this format into the following data class using annotations:
public class MarketStatObject {
private int id;
private MarketStatObjectStats buy;
private MarketStatObjectStats sell;
private MarketStatObjectStats all;
....
}
public class MarketStatObjectStats {
// values
private long volume;
private double average;
private double minimum;
private double maximum;
private double stddev;
private double median;
private double percentile;
....
}
Now, the annotations for the MarketStatObjectStats object should be quite self explanatory but I'm caught up with the XML structure of "type" being a wrapper for the buy/sell/all statistics.
What would be the best way to solve this? I don't want type id to be a list since that's totally unnecessary...
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You could use MOXy's #XmlPath extension for this use case:
MarketStatObject
package forum17988539;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="evec_api")
#XmlAccessorType(XmlAccessType.FIELD)
public class MarketStatObject {
#XmlPath("marketstat/type/#id")
private int id;
#XmlPath("marketstat/type/buy")
private MarketStatObjectStats buy;
#XmlPath("marketstat/type/sell")
private MarketStatObjectStats sell;
#XmlPath("marketstat/type/all")
private MarketStatObjectStats all;
}
Demo
The standard JAXB runtime APIs are used to conversion your XML to/from Objects.
package forum17988539;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(MarketStatObject.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17988539/input.xml");
MarketStatObject marketStat = (MarketStatObject) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(marketStat, System.out);
}
}
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
#XmlRootElement(name = "evec_api")
public class EvecApi {
#XmlElement MarketStat marketstat;
}
public class MarketStat {
#XmlElement MarketStatObject type;
}
public class MarketStatObject {
#XmlAttribute Integer id;
#XmlElement MarketStatObjectStats buy;
#XmlElement MarketStatObjectStats sell;
#XmlElement MarketStatObjectStats all;
}
public class MarketStatObjectStats {
#XmlElement long volume;
#XmlElement double average;
#XmlElement double minimum;
#XmlElement double maximum;
#XmlElement double stddev;
#XmlElement double median;
#XmlElement double percentile;
}
#XmlType
#XmlAccessorType(XmlAccessType.FIELD) // here I need this access
public class User implements Serializable
{
// ...
#XmlTransient
private Set<Values> values;
// ...
#XmlElement
private Set<History> getXmlHistory()
{
return new CustomSet<Values, History>(Values);
}
private void setXmlHistory(final Set<History> aHistory)
{
this.values = new HashSet<Values>();
}
}
When I am create User object in Java code and after create XML, then all normally.
But when I try to get User-object from XML, then field values always null. So setter not working here. May be setter needs some annotation too?
XML looks like
<user>
...
<xmlHistory>
// ... record 1
</xmlHistory>
<xmlHistory>
// ... record 2
</xmlHistory>
</user>
I do not believe that this is a JAXB problem, as the following model will work:
package forum10617267;
import java.io.Serializable;
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlType
#XmlAccessorType(XmlAccessType.FIELD) // here I need this access
public class User implements Serializable {
#XmlTransient
private Set<History> history = new HashSet<History>();
#XmlElement
private Set<History> getXmlHistory() {
return history;
}
private void setXmlHistory(final Set<History> aHistory) {
this.history = aHistory;
}
}
The problem you are seeing is a result of the logic you have in your get/set methods. Since your values field is not initialized, I am not sure how CustomSet would be able to update it.
package forum10617267;
import java.io.Serializable;
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlType
#XmlAccessorType(XmlAccessType.FIELD) // here I need this access
public class User implements Serializable {
#XmlTransient
private Set<Values> values;
#XmlElement
private Set<History> getXmlHistory() {
return new CustomSet<Values, History>(values);
}
private void setXmlHistory(final Set<History> aHistory) {
this.values = new HashSet<Values>();
}
}
I have a class that I wish to populate with content from an XML file using JAXB. My XML file looks similar to this:
<root>
<mylist>
<item id="1">First Item</item>
<item id="2">Second Item</item>
</mylist>
</root>
My JAXB annotated classes look like:
#XmlRootElement
class MyParentClass {
// I always populate this with a TreeSet
private Set<MyFieldItem> items;
public void setItems(Set<MyFieldItem> items) {
this.items = items;
}
#XmlElementWrapper("mylist") #XmlElement("item")
public Set<MyFieldItem> getItems() {
return items;
}
}
class MyFieldItem implements Comparable<MyFieldItem> {
private Integer id;
private String value;
public void setId(Integer id) {
this.id = id;
}
#XmlAttribute
public Integer getId() {
return id;
}
public void setValue(String value) {
this.value = value;
}
#XmlValue
public String getValue() {
return value;
}
public int compareTo(MyfieldItem o) {
return this.id.compareTo(o.getId());
}
}
I find that this arrangement serialises my objects to XML correctly, but when I try to convert it back the TreeSet I use becomes a HashSet.
In theory my collection could be fixed to a TreeSet (which does fix the problem), but I'd rather configure JAXB correctly and defer that logic elsewhere. How do I tell JAXB to build a TreeSet instead?
The easiest way to solve this problem is pre-initialize your Set property to the appropriate implementation type, and your JAXB implmentation (Metro, EclipseLink MOXy, Apache JaxMe, etc) will use that instead of creating a new Set:
package forum7104810;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="root")
class MyParentClass {
// Initialize this property with an instance of the desired type.
private Set<MyFieldItem> items = new TreeSet<MyFieldItem>();
public void setItems(Set<MyFieldItem> items) {
this.items = items;
}
#XmlElementWrapper(name="mylist")
#XmlElement(name="item")
public Set<MyFieldItem> getItems() {
return items;
}
}
For More Information
http://blog.bdoughan.com/2011/01/jaxb-and-choosing-list-implementation.html