Parse XML to different data structure with JAXB - java

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;
}

Related

Serializing multiple tiers of XML elements

Let's say I'm trying to create an XML document out of an object. Is this possible using JAXB annotations on a single Food class, or do I need to create inner classes for Cost and Flavor?
I know I can use #XmlElement or #XmlAttribute to set up immediate children of my root element. However, I'm not sure if/how to create the <cost> and <Flavor> tags as I show here.
<Food>
<cost amt=13.5 unit=USD/>
<Flavor spicy=5>It tastes good</Flavor>
</Food>
#XmlRootElement("Food")
public class Food {
private float amount;
private String units;
private String flavorType;
private STring flavorDescription;
}
Add a new Java class Cost:
public class Cost
{
#XmlAttribute
double amt;
#XmlAttribute
String unit;
}
And exdend class Food
#XmlRootElement
public class Food {
private float amount;
private String units;
private String flavorType;
private String flavorDescription;
private Cost cost;
...
you could also use something like this for Flavor class
public class Flavor {
private long spicy;
private String shortDesc;
#XmlValue
public String getShortDesc() {
return shortDesc;
}
public void setShortDesc(String shortDesc) {
this.shortDesc = shortDesc;
}
#XmlAttribute
public Long getSpicy() {
return spicy;
}
public void setSpicy(long spicy) {
this.spicy= spicy;
}
}

REST API with Interfaces - JAXB

I am using an implementation of an interface in Java.
For eg: There can be many PaymentTypes like Credit Card, Mobile etc.
I am making a REST API which contains an interface- how do I map this in JAXB, currently it gives me JAXBException occurred : 2 counts of IllegalAnnotationExceptions.
Currently I am using Apache-CXF and JAXb
#XmlRootElement
public class Payment {
#XmlElement
private PaymentType paymentType;
#XmlElement
private long price;
public Payment() {
}
public Payment(final PaymentType paymentType, final long price) {
super();
this.paymentType = paymentType;
this.price = price;
}
}
#Path("/trial")
public class TrialService {
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Payment> getPayments() {
final List<Payment> payments = new LinkedList<Payment>();
final CreditCardDetails creditCard = new CreditCardDetails(
"8767798778", "123", 12, 2016);
final Payment payment = new Payment(creditCard, 10);
payments.add(payment);
return payments;
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public PaymentDetails startPayment(final PaymentDetails paymentDetails) {
return paymentDetails;
}
}
public class CreditCardDetails implements PaymentType {
#XmlElement
private String creditCardNumber;
#XmlElement
private String cvv;
#XmlElement
private int expirationMonth;
#XmlElement
private int expirationYear;
public CreditCardDetails() {
}
#SuppressWarnings("javadoc")
public CreditCardDetails(
// final BillingAddress billingAddress,
final String creditCardNumber, final String cvv,
final int expirationMonth, final int expirationYear) {
super();
this.creditCardNumber = creditCardNumber;
this.cvv = cvv;
setExpirationMonth(expirationMonth);
setExpirationYear(expirationYear);
}
}
How should I be mapping this or should I be using an entirely different approach?
Edits:
For the POST method I am receiving a payment. Payment could contain any object CreditCard, Wallet etc. What annotation should I provide so that it is desirialized correctly.
Currently it throws a JAXB exception.
The full error message which you've got is:
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions PaymentType is an interface, and JAXB can't handle interfaces.
You need to use concreate class for your elements or point it in type attribute of #XmlElement annotation:
#XmlElement(type = CreditCardDetails.class)
private PaymentType paymentType;
If you have more classes that uses PaymentType interface then you may use the following solution:
#XmlAnyElement
#XmlElementRefs({
#XmlElementRef(type=CreditCardDetails.class),
#XmlElementRef(type=Wallet.class)
})
PaymentType paymentType;
The list of #XmlElementRefs can have any number of elements but all possibilities must be listed. CreditCardDetails and Wallet must be annotated with #XmlRootElement.
You can skip #XmlElementRefs annotation:
#XmlAnyElement(lax=true)
PaymentType paymentType;
but in that case make sure you have any required class in JAXB context, if you do not use registry annotate your class with PaymentType field with #XmlSeeAlso({CreditCardDetails.class, Wallet.class}).

JAXB marshalling and inheritance

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.

JAXB unmarshall to multiple pojo's

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

Simple Java to XML example

I've read a time ago about generate xml from Java using annotations, but I'm not finding a simple example now.
If I want to make a xml file like:
<x:element uid="asdf">value</x:element>
from my java class:
public class Element {
private String uid = "asdf";
private String value = "value";
}
Which annotations should I use to perform that? (I have a xml-schema, if this helps the generation)
--update
The javax.xml.bind.annotation package have the annotations, "but I still haven't found what I'm looking for": an exemple of usage.. :)
Found it:
import java.io.FileOutputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
public class JavaToXMLDemo {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Employee.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Employee object = new Employee();
object.setCode("CA");
object.setName("Cath");
object.setSalary(300);
m.marshal(object, System.out);
}
}
#XmlRootElement
class Employee {
private String code;
private String name;
private int salary;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int population) {
this.salary = population;
}
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<code>CA</code>
<name>Cath</name>
<salary>300</salary>
</employee>
From: http://www.java2s.com/Code/JavaAPI/javax.xml.bind.annotation/javaxxmlbindannotationXmlRootElement.htm
For the benefit of anyone else hitting this thread, I imagine you did the following:
#XmlRootElement
public class Element {
#XmlAttribute
private String uid = "asdf";
#XmlValue
private String value = "value";
}
For More Information
http://bdoughan.blogspot.com/2011/06/jaxb-and-complex-types-with-simple.html
There are various tools that you can use to do this. XStream (http://x-stream.github.io/) is a reasonably easy tool to use that allows you to use annotations to determine the schema of XML that is created.

Categories