Persisting data in java with jaxb for a standalone program - java

I'm working on a hobby/learning project, and I need to persist some data, but I don't want to use a database. After some research, I found that using JAXB could be a good idea, but I can't seem to wrap my head around using it. I read tutorials, and the documentation as well, but it just doesn't click for me.
The program is a receipt storing/budgeting type of thing, so I'd need to save the receipts somehow.
The relevant classes look like this so far:
package classes;
import java.time.*;
import java.util.ArrayList;
import java.util.List;
public class Receipt {
private String shop;
private LocalDate date;
private Double total;
private List<Entry> items;
//...contructor,getters & setters
and
package classes;
public class Entry {
private String name;
private Double price;
//...contructor,getters & setters
And what I'd like to have looks kinda like this:
<Receipts>
<Receipt>
<Shop>Tesco</Shop>
<Date>2016-06-03</Date>
<Total>200</Total>
<Items>
<Item>foo 100</Item>
<Item>bar 100</Item>
</Items>
</Receipt>
...
</Receipts>
So I guess, I have 2 questions:
1. Am I on the right track with JAXB, is it a right way of persisting data for a standalone application?
2. How can I do it? I don't understand all the annotations and schemas and stuff.
I'd appreciate any help, pointing me to a good tutorial, or writing a very didactic guide.
Thank you

JAXB is not is not a persistence engine. You can use it to convert XML to Java objects or objects to XML. The data can be persisted on files or even databases. JAXB does not replace a database. You have no searches, relationships between different entities, indexes, XML data is loaded entirely, etc.
If you want to use it in your learning project these are the main parts. I include only the annotations and changes to code to map your model
Java objects
//Added XmlRootElement and XmlElement for list
#XmlRootElement(name = "Receipts")
public class Receipts {
private List<Receipt> receipts;
#XmlElement(name = "Receipt")
public List<Receipt> getReceipts() {
//Added XmlElementWrapper and XmlElement for list
public class Receipt {
private String shop;
private Date date; //Changed to Date because marshalling is directly supported
private Double total;
private List<Entry> items ;
#XmlElementWrapper(name="Items")
#XmlElement(name = "Item")
public List<Entry> getItems() {
//No changes
public class Entry {
private String name;
private Double price;
Marshall (Java to XML)
JAXBContext jaxbContext = JAXBContext.newInstance(Receipts.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(receipts, new File(file);
Unmarshaller (XML to Java)
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Receipts receiptsUnmarshalled = (Receipts) jaxbUnmarshaller.unmarshal(file);

Related

JAXB edit List getter?

I have my data model in the form of XSD files from which I then generate the corresponding Java files from xjc using command line.
When I generate JAXB classes from an XSD, List type elements gets a getter method generated for them (with no corresponding setter method), as follows:
public List<Type> getElement3() {
if (element3 == null) {
element3 = new ArrayList<Type>();
}
return this.element3;
}
I have lot of fields in almost every file generated from XSD of List type.
USE case:
Now, I don't want the getter to be generated with the null check. My application code has logic in which the getters of every fields are called often, which leads to their initialization to empty List.
Then while marshaling I have to stop the empty list to pass in the payload to avoid lot of empty lists being sent over the wire.
PS: I have a use case where Empty List is set explicitly by the user, the server has to delete certain items at the back-end. So the differentiation whether the value is explicitly set by the user or is set just because the getter for the List was called during accessing the field.
So, How to make JAXB generate a getter without a null check ??
As, editing the generated java files after compilation will be cumbersome as it's there in lot of files and we have XSD versions getting updated and would have to perform the editing every time a new version comes up.
At first, I would think of using a custom JAXB Binding, but I canĀ“t think of any that would meet this requirement.
In this case, perhaps you could use a wrapper class:
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "employees")
#XmlAccessorType (XmlAccessType.FIELD)
public class Employees
{
#XmlElement(name = "employee")
private List<Employee> employees = null;
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
And then define your business object:
#XmlRootElement(name = "employee")
#XmlAccessorType (XmlAccessType.FIELD)
public class Employee
{
private Integer id;
private String firstName;
private String lastName;
private double income;
//Getters and Setters
}
You will have to control the initialization of the list yourself when generating the objects to be marshalled:
Employees employees = new Employees();
employees.setEmployees(new ArrayList<Employee>());
Source of this example: Here
I've created a small example which you could try. Although it doesn't remove the null-check from the original getter, it is a jaxb-plugin which creates an additional method isXXXNull and allows you to test this case before accessing the getXXX-method. Take a look at the example here

Spring MVC - receiving XML Objects and deserialzing into Collection

I'm attempting to deserialize an XML payload (body of a SOAP message, but nothing else), with a specific hierarchy of tags / objects. When attempting to aggregate unwrapped objects into a List, a MismatchedInputException is thrown.
Example Payload
<libraryRequest>
<libraryProfile>
<libraryId>
<libraryName>
<newBookInfo>
<bookId>...</bookId>
<bookTitle>...</bookTitle>
<datePublished>...</datePublished>
</newBookInfo>
<currentBooks>
<bookId>...</bookId>
<bookTitle>...<bookTitle>
<datePublished>...</datePublished>
</currentBooks>
<currentBooks>
<bookId>...</bookId>
<bookTitle>...<bookTitle>
<datePublished>...</datePublished>
</currentBooks>
<currentBooks>...</currentBooks>
</libraryProfile>
</libraryRequest>
Java objects are
public class LibraryRequest {
private LibraryProfile libraryProfile;
#XmlElement(name = "libraryProfile")
public LibraryProfile getLibraryProfile(){
...
}
// setters
public class LibraryProfile {
// constructors, getters & setters for primitive types
private List<BookInfo> bookInfos;
public List<BookInfo> getBookInfo(){
return this.BookInfos;
}
// rest of the functions
My issue is that I don't know how many currentBooks tags will come in the XML payload, and they don't come in a wrapper element. I need to keep track of each currentBook element, which is why I was using a Collection, but I am not able to properly fill the collection with the information contained within the currentBooks tags.
Would I be able to use JAXB to group the XML sequence into a Java Collection/List, and if not would I be able to use Jackson's XML functionality to group the unwrapped XML tags into a Java Collection?
The main goal is to use have an XML request come into a Spring Controller and have the XML sequence properly deserialized into a Java List / Collection. Any advice would help.
I'm using Spring Boot 1.5.8 (later version was giving me trouble in a different way), and Jackson version 2.9.5
This is based on the XmlElement explanation from actimem.com.
The mechanics explained:
- #XmlElement is only needed if the field name is not equal to the xml tag name.
- If you would like to rename your field newBookInfo to newestBook but without changing the xml you'd simply rename your field and annotate it with #XmlElement(name="newBookInfo")
- #XmlElementWrapper is explicitly not used to advice JAXB it should search for the list tags directly in the parent node
The XML represenation classes Book
#XmlAccessorType(XmlAccessType.FIELD)
public static class Book {
private String bookId;
private String bookTitle;
// ... accessors and toString
}
and LibraryProfile
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public static class LibraryProfile {
private String libraryId;
private String libraryName;
private Book newBookInfo;
// the trick is NOT to use #XmlElementWrapper here
private List<Book> currentBooks;
private String foobar; // just to show a tag after the list
// ... accessors
}
The input based on your question (I skipped the <libraryRequest> to keep the example short)
<libraryProfile>
<libraryId>1</libraryId>
<libraryName>library of alexandria</libraryName>
<newBookInfo>
<bookId>42</bookId>
<bookTitle>the answer</bookTitle>
</newBookInfo>
<currentBooks>
<bookId>2</bookId>
<bookTitle>the second</bookTitle>
</currentBooks>
<currentBooks>
<bookId>1</bookId>
<bookTitle>the first</bookTitle>
</currentBooks>
<foobar>test-foo</foobar>
</libraryProfile>
And here the testing class:
package com.stackoverflow.answer;
import javax.xml.bind.JAXB;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringReader;
public class Tester {
public static final String INPUT = "..."; // here goes your xml
public static void main(String[] args) throws Exception {
LibraryProfile lib = JAXB.unmarshal(new StringReader(INPUT), LibraryProfile.class);
System.out.println(lib.getLibraryName() + " currently contains");
System.out.println(lib.getCurrentBooks());
System.out.println("the newest book is: " + lib.getNewBookInfo());
}
}
The output is now
library of alexandria currently contains
[Book{bookId='2', bookTitle='the second'}, Book{bookId='1', bookTitle='the first'}]
the newest book is: Book{bookId='42', bookTitle='the answer'}

XML Deserialization errors using Simple XML

In one of my applications I'm using thegamesdb.net's APIs to download the cover images of some games. To get informations from XML files I'm trying to implement Simple XML library to deserialize.
This is my example query result (name=Splatterhouse,platform=TurboGrafx 16):
<?xml version="1.0" encoding="UTF-8" ?>
<Data>
<Game>
<id>3776</id>
<GameTitle>Splatterhouse</GameTitle>
<ReleaseDate>01/01/1990</ReleaseDate>
<Platform>TurboGrafx 16</Platform>
</Game>
</Data>
And this is how I implemented my classes in java:
#Root
public class Data{
#ElementList(inline=true)
private List<Game> list; //This is correct, it's only in the example that I've only one result.
public List<Game> getGames(){
return list;
}
}
#Root
public class Game{
#Element
private int id;
#Element
private String GameTitle;
#Element(required=false)
private String ReleaseDate;
#Element(required=false)
private String Platform;
public String getTitle(){
return GameTitle;
}
public int getId(){
return id;
}
}
To read from the xml file, I call the serializer:
String xml = getXmlFromUrl(URL);
Serializer serializer = new Persister();
Data data = serializer.read(Data.class, xml);
What's wrong?
UPDATE: It came out that the tutorials on Simple Site are a bit incomplete since they don't set properly annotations, these annotations solved all my issues, plus all the warnings are "normal", it's simple stating that jaxp doesn't exist on android and is going to use other tools (XmlPullParser?).
This is my proper code:
#Root(name = "Data")
public class Data {
#ElementList(inline = true, required = false)
private List<Game> list = new ArrayList<Game>();
public List<Game> getGames() {
return list;
}
}
#Root(name = "Game", strict = false)
public class Game {
#Element(name = "id")
private int id;
#Element(name = "GameTitle")
private String GameTitle;
public String getTitle() {
return GameTitle;
}
public int getId() {
return id;
}
}
You could also use JAXB to (de)serialize XMLs (or to (un)marshal as they call it). It comes with java - so there's no need for an additional library. Here you have an example.
I believe you need to declare your classes in their own files (Data and Game), or mark them as static. If you declare them as inner classes they can't be constructed by the library because they can only exist within an instance of the parent class.
It's nothing to do with your XML, it seems like something is wrong with the library you're using (not being able to create a new instance of a certain class). Consider re-downloading it and adding it to your project again.
The tutorials on SimpleXML website show an incomplete and inaccurate use of annotations.
I updated my code, check at the bottom of my answer to get the proper implementation. You're strictly adviced to use name="Name" in your annotations, and if you don't need all the Elements/Attributes use strict=false on the class and required=false on your elements or attributes.

How to create a Camel route which takes XML and bind some data to JPA annotated POJO?

I am new to Apache Camel and mock testing so here it goes...
I have an XML with no XSD schema which I have no influence on. Child elements of this XML hold data which I want to bind to my business pojo. This POJO (WeatherCurrent) is already JPA annotated and I was thinking of adding JAXB annotation so the splitted XML could be mapped to my POJO.
As this XML has a root element and I only want its childs (metData) I have a problem how to annotate my POJO as I can not use #XmlRootElement.
This is partialy described here: http://camel.apache.org/splitter.html at Streaming big XML payloads using Tokenizer language chapter. My POJO is like order xml element in that example. I need just a few elements out of metData xml element to map to my POJO fields.
There is also a chapter Partial marshalling/unmarshalling at http://camel.apache.org/jaxb.html but there is no JAVA DSL example (a must), nor how to annotate the pojo to work with XML fragments.
So far I have this test code:
import java.io.File;
import org.apache.camel.EndpointInject;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.converter.jaxb.JaxbDataFormat;
import org.apache.camel.spi.DataFormat;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class WeatherCurrentTest extends CamelTestSupport {
#EndpointInject(uri = "file:src/test/resources")
private ProducerTemplate inbox;
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
DataFormat jaxbDataFormat = new JaxbDataFormat("com.mycompany.model.entities.weather");// WARNING two packages for JaxbDataFormat
from("file:src/test/resources/?fileName=observation_si_latest.xml&noop=true&idempotent=false")
.split()
.tokenizeXML("metData")
.unmarshal(jaxbDataFormat)
.to("mock:meteo");
}
};
}
#Test
public void testMetData() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:meteo");
mock.expectedMessageCount(9);
File meteo = new File("src/test/resources/observation_si_latest.xml");
String content = context.getTypeConverter().convertTo(String.class, meteo);
inbox.sendBodyAndHeader(content, Exchange.FILE_NAME, "src/test/resources/observation_si_latest.xml");
mock.assertIsSatisfied();
}
}
The XML (observation_si_latest.xml) comes in this form:
<?xml version="1.0" encoding="UTF-8"?>
<data id="MeteoSI_WebMet_observation_xml">
<language>sl</language>
<metData>
<domain_altitude>55</domain_altitude>
<domain_title>NOVA GORICA</domain_title>
<domain_shortTitle>BILJE</domain_shortTitle>
<tsValid_issued>09.03.2012 15:00 CET</tsValid_issued>
<t_degreesC>15</t_degreesC>
</metData>
<metData>
<domain_meteosiId>KREDA-ICA_</domain_meteosiId>
I left out lots of elements of the metData elements for brevity. I want to map (amongs others) domain_title to my JPA annotated POJO's station field and later save it database, hopefully all in one smart and short Camel route.
The POJO (no JAXB annotations yet):
#Entity
#Table(name="weather_current")
public class WeatherCurrent implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String station;
#Temporal( TemporalType.TIMESTAMP)
#Column(name="successfully_updated")
private Date successfullyUpdated;
private short temperature;
#Column(name="wind_direction")
private String windDirection;
}
I also left out lots of fields and methods.
So the idea is to map the value of the *domain_title* to WeatherCurrent POJO's station field and to do so for each metData element and save a list of WeatherCurrent objects to database.
Any advice on the best way on how to implement this is welcome.
It turns out I had one wrong assumption of not being able to use the #XmlRootElement. The route and the test passes with success after I annotated the POJO and added jaxb.index file beside it. Will post the solution later or tomorrow as I am on a train now.
Hours later...
The JAXB annotations on the POJO (on top of JPA ones):
#Entity
#Table(name="weather_current")
#XmlRootElement(name = "metData")
#XmlAccessorType(XmlAccessType.FIELD)
public class WeatherCurrent implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#XmlElement(name = "nn_shortText")
private String conditions;
#XmlElement(name = "rh")
private short humidity;
#XmlElement(name = "msl")
private short pressure;
#Column(name="pressure_tendency")
#XmlElement(name = "pa_shortText")
private String pressureTendency;
#Temporal( TemporalType.TIMESTAMP)
#XmlElement(name = "tsValid_issued")
private Date published;
#XmlElement(name = "domain_longTitle")
private String station;
enabled me to get a list of WeatherCurrent Exchage objects. Just for test I routed each one to my EchoBean to print out one property:
.unmarshal(jaxbDataFormat).bean(EchoBean.class, "printWeatherStation")
and EchoBean:
public class EchoBean {
public String printWeatherStation(WeatherCurrent weatherCurrent) {
return weatherCurrent.getStation();
}
}
nicely prints out the names of the weather stations with the log Camel component.
The one undocumented thing that bothered me was that I had to put this jaxb.index file next WeatherCurrent java source although at http://camel.apache.org/jaxb.html it clearly says that jaxb context is initialized with
DataFormat jaxb = new JaxbDataFormat("com.acme.model");

How can I marshall a single java bean into a complex XML document with existing annotations?

I have a single java bean, which is already annotated for JPA, that I also wish to store as XML, specifically FIXML. The goal is to manage the mapping from bean to XML with annotations.
I see related topics online about specifying a schema and letting JAXB generate classes, but I don't want to do that.
I've been looking at using JAXB annotations, but it seems that I'll need to make new classes for each child element. I'm trying to stay away from that, and let the annotations show how to construct the child elements. JAXB does not seem to want to do this.
Is this possible, and how? Do I need to make my own annotations and forget about JAXB?
Concrete example
Bean:
#Entity
#XmlRootElement(name="FIXML")
#XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
private String account;
private String senderCompID;
#Column(name="ACCOUNT", nullable=true, length=64)
#XmlAttribute(name="Acct")
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name="SENDER_COMP_ID", nullable=true, length=200)
#XmlAttribute(name="SID")
public String getSenderCompID() {
return this.senderCompID;
}
public void setSenderCompID(String senderCompID) {
this.senderCompID = senderCompID;
}
}
Parsing:
JAXBContext context = JAXBContext.newInstance(ExecutionReport.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); //pretty print XML
marshaller.marshal(executionReport, System.out);
Desired resulting XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML>
<ExecRpt Acct="account_data">
<Hdr SID="sender"/>
</ExecRpt>
</FIXML>
Current resulting XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML Acct="account_data" SID="sender"/>
Clearly I'm not providing enough information to map the child elements yet, but I'm also not sure how to provide it. I want to add some #XmlElement annotations, but I don't have child objects, all the data is in this class.
The upside is that my XML isn't much more complicated than this example; there are only a handful of elements, and they only appear once per message. The thing that's giving me trouble is getting multiple elements out of a single bean.
You can use the #XmlPath extension in EclipseLink JAXB (MOXy) for this, I'm the tech lead.
Model Class
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#Entity
#XmlRootElement(name="FIXML")
#XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
private String account;
private String senderCompID;
#Column(name="ACCOUNT", nullable=true, length=64)
#XmlPath("ExecRpt/#Acct")
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name="SENDER_COMP_ID", nullable=true, length=200)
#XmlPath("ExecRpt/Hdr/#SID")
public String getSenderCompID() {
return this.senderCompID;
}
public void setSenderCompID(String senderCompID) {
this.senderCompID = senderCompID;
}
}
Demo Code
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExecutionReport.class);
ExecutionReport er = new ExecutionReport();
er.setAccount("account_data");
er.setSenderCompID("sender");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(er, System.out);
}
}
Resulting XML
<?xml version="1.0" encoding="UTF-8"?>
<FIXML>
<ExecRpt Acct="account_data">
<Hdr SID="sender"/>
</ExecRpt>
</FIXML>
Specifying the EclipseLink JAXB (MOXy) Implementation
To specify MOXy as the JAXB implementation you need to add a file called jaxb.properties in with your ExecutionReport class with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For More Information
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
I don't know Jaxb annotations but if you ask for an attribute in ExecRpt it seems normal to have an attribute in ExecRpt no?
I think you expect a bit too much of these annotations. Don't you also want an annotation that would take a string, split it with a separator and generate a list of child elements or something?
And it seems to me a bad design to put these annotations directly on JPA entities. One day you could have to do some database changes for performances issues for exemple and you could not be able to generate the xml you want anymore. Why not transforming your jpa entity to a given structure jaxb friendly so that you keep the db and marshalling appart? Thus on change you would just have to modify the transformer.

Categories

Resources