I'm trying to rewrite some API serialization from custom mappers to annotation-based style and faced with one hard mapping (which was earlier custom-serialized to json and xml separately) that I can not "translate" to. (Serialization is made with Jackson.)
In the POJO we have a collection, e.g.
class Data {
Set<Integer> tags;
}
which should be serialized in xml like:
<tags>
<tag id="1"/>
<tag id="2"/>
</tags>
and in json like:
{
"tags":[1,2]
}
Strait method with
#XmlElementWrapper(name="tags")
#XmlElement(name="tag")
gives good json, but incorrect xml like
<root>
<tags>
<tag>1<tag/>
<tag>2<tag/>
</tags>
</root>
cause there is no attribute specification.
I tried to wrap a bit with:
class Data{
#XmlElementWrapper(name="tags")
#XmlElement(name="tag")
Set<Tag> tags;
}
class Tag{
#XmlAttribute(name="id")
Integer id;
}
But this produces unwanted key in json format, like:
"tags":[
{"tag":{"id":1}},
{"tag":{"id":2}}
]
Ok, then. I tried to specify custom json serializer(implementing JsonSerializer and injecting with #JsonSerialize(using = ...) ), but seems it also affects xml "render".
Is it possible to do the trick with annotations only? Or mb is it possible somehow use default json serialization and custom xml serializtaion for some class? .e.g.
use custom xml serialization only for Reasons class in such way
class Data {
#XmlElement("tags")
Reasons tags;
}
but let all surrounding data be "render" with general strategy.
Simply create a getter annotated with #JsonValue will tell Jackson to produce a single value, without any field name.
This mapping:
#XmlRootElement
public class Data{
public Set<Tag> tags;
}
public class Tag{
#XmlAttribute
public Integer id;
#JsonValue
public Integer getId() {
return id;
}
}
Will then produce:
{"tags":[2,1]}
And:
<data><tags id="2"/><tags id="1"/></data>
PS: You're using JAXB annotations, i don't think Jackson will honor them.
To get the XML result above, you need to use JAXB:
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Data.class, Tag.class);
Marshaller m = context.createMarshaller();
m.marshal(value, writer);
System.out.println(writer.getBuffer().toString());
As a final note, i'm not super fan of using a single mapping for multiple representations. It's probably good for simple stuff, but if your code grows more complex than this, i strongly suggest you create two sets of classes (one for XML mapping and one for JSON), maybe with a common interface.
Related
I trying to use the JAXB annotation to convert the JSON data to XML. I have the JSON somewhat like this:
{
"Employee": {
"name": "Tanmay",
"Email": "tanmaypatil#xyz.com",
"Address": {
"City": "Florida",
"State": "Miami"
}
}
}
I would like to use marshalling to create the XML which would look something like this:
(Please note the extension tag which is not present in JSON but I need to add in XML)
<Company>
<Employee>
<FirstName>Tanmay</FirstName>
<extension>
<Address>
<City>Florida</City>
<State>Miami</State>
</Address>
</extension>
</Employee>
</Company>
I have the following classes:
My main class would actually read the JSON data and marshal it to XML. I have used Jackson to read the JSON data for simplicity purpose I have removed that code:
public class Main
{
public static void main(String[] args) {
JsonFactory jsonFactory = new JsonFactory();
JsonParser jsonParser = jsonFactory.createParser(new File(Main.class.getClassLoader().getResource("InputEPCISEvents.json").toURI()));
//I am using the JAKSON jsonParser to read the JSON file and automatically map it to the child classes
//Ommiting the JACKSON JsonParser code for simplicity purpose
JAXBContext context = JAXBContext.newInstance(Employee.class);
Marshaller mar = context.createMarshaller();
mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mar.marshal(eventInfo, System.out);
System.out.println("-----------------");
}
}
Following is my Parent class which would be mapped to Incoming JSON:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlTransient
public class Employee{
private String name;
private String email;
private Address address;
//getters and setters are ommited
//Noargs and allargs constuctor ommited
}
Following is my Address class:
#XmlRootElement(name = "Employee")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Extension", propOrder = { "name","email","city","state"})
private class Address extends Employee{
#XmlElement(name = "extension")
private Extension extension;
public Extension getExtension() {
extension.setCity(getCity());
extension.setState(getState());
return extension;
}
public Extension setExtension(Extension extension) {
this.setCity(extension.getCity());
this.setState(extension.getState());
this.extension = extension;
}
}
Following is the Extension class that I have created as Extension tag will not be present within the incoming JSON but I need to add it to the XML during the marshaling:
#XmlAccessorType(XmlAccessType.FIELD)
public class Extension{
private String city;
private String state;
//getters and setters are ommited
//Noargs and allargs constuctor ommited
}
I would like to know how to do the following things using the JAXB. I am fine with creating some additional classes or modifying the existing class but I cannot change the incoming JSON or XML that I am creating as they are standard format.
How can I map the address,city,state from JSON to Class object. If the elements are present within the Address.class then it can be mapped automatically but since I want the extension tag in XML I have moved them to my Extension.class hence the JAXB cannot recognize it and its not mapping to objects.
How to add the additional extension tag to the XML that will be created during the marshalling. As we can see the JSON does not contain the extension tag but I need it in the XML. If its collection then I can use the #XmlElementWrapper but in my case it's not Collection.
I tried searching a lot but could not find anything that helps me. So based on my understanding I have done above mentioned things. Any help or suggestions would be really appreciated.
I think you have to register all used classes in your JAXBContext
JAXBContext context = JAXBContext.newInstance(Employee.class, Extension.class, ...)
Alternatively, you can also pass the "root" package of all your classes:
JAXBContext context = JAXBContext.newInstance("com.demo.beans")
Finally, I was able to do this using the Moxy #XmlPath. If you are using the Moxy then you can use the #XmlPath and add the additional tags and do not have to create any additional classes.
For example here in this case I do not have to create Extension.class but stil I can add the extension tag to my XML. Moxy is and implementation based on JAXB.
I spent a lot of time trying to figure out a lot of things. Hence, posting the same as it can be helpful to someone in the future.
You can find how to configure Moxy from here. Do not worry its very simple.
You can find how to use #XmlPath from here.
There you go with few very simple steps you can get the wrapper tags in your XML for even non-collection items. Also, no need to create multiple additional classes.
I've got a very simple problem here, but after using Google for over an hour now, I still cannot find a good solution and it starts to cost too much money..
In my app I use REST as an API, basically only with JSON as payload type, and Enunciate for documentation of the API. As you might know, enunciate will generate an xsd schema from the classes. I am therefore in the situation, that I need to configure all the DTO's for Jackson/JSON handling with the suitable annotations, and also with JAXB annotation to ensure the generated schema is correct and can be parsed with XJC into the correct classes!
Although that is not very difficult to achieve and works flawless in most of the cases, I have a simple but somehow special case, in which it completely fails.
Assuming the following classes:
#JsonRootName(value = "location")
public class Location {
private String label;
#JsonUnwrapped
private Address address;
// constructors, getters, setters ommited..
}
//// new file
public class Address{
private String city;
private String street;
private String postCode;
}
This works 100% with Jackson/JSON. The embedded Address object will be unwrapped so that the JSON looks like this:
{
"label":"blah",
"street":"Exchange Road",
"city":"Stacktown"
"postCode":"1337"
}
This works forth and back with Jackson.
JAXB on the other hand is able to parse (most of) the Jackson annotations, so that generally you wont have problems with using simple objects in both worlds. #JsonUnwrapped though sadly is NOT supported by JAXB, and strangely that (from my POV) quite simple usecase seems to be not reflectable with any JAXB annotation at all.
What happens is, that the generated schema contains the embedded Address object, without any attributes/elements. Therefore the class generated by XJC will contain that Address object reference. And this ultimately leads to erroneous JSON from a app that will use the schema to generate objects...
Any ideas?
The JAXB (JSR-222) specification does not define an equivalent to Jackson's #JsonUnwrapped. For a long time we have offered that functionality in the EclipseLink MOXy implementation of JAXB via our #XmlPath extension.
#XmlPath(".")
private Address address;
For More Information
I have written more about MOXy's #XmlPath extension on my blog:
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
I have an XML document that looks something like the following:
Note that I cannot change the schema because it is part of a standard XML Schema (Library of Congress METS).
<amdSec ID="AMDSEC001">
<digiprovMD ID="DMD001">
<mdWrap MDTYPE="OBJECT">
<xmlData>
<object xsi:type="file">
.....
</object>
</xmlData>
</mdWrap>
</digiprovMD>
<digiprovMD ID="DMD001_EVENT">
<mdWrap MDTYPE="EVENT">
<xmlData>
<event xsi:type="event">
.....
</event>
</xmlData>
</mdWrap>
</digiprovMD>
</amdSec>
As you can see, the inner element <mdWrap> can contain elements of different types; in this case they're <event> and <object>, but it isn't constrained to just those two types. Creating two classes (like below), marshals okay, but this doesn't work for unmarshalling.
class ObjectMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "object")
List<MyObject> object; //Wrapped in list to use #XmlElementWrapper
}
class EventMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "event")
List<MyEvent> event; //Wrapped in list to use #XmlElementWrapper
}
What can I do so that JAXB unmarshals the correct "type" of MDWrap?
I think, the best solution in this case is a generating POJO classes using XJC tool.
Download XSD file which describe XML file.
Using XJC tool convert XSD file into POJO classes. If XSD is not correct - fix it.
Make some changes if you need in generated classes.
Use this classes in marshalling / unmarshalling process.
I was able to figure out the solution, and it's much simpler than I initially thought (which speaks to my relative inexperience with XML and JAXB). By creating my MDWrap class in the following way
class MDWrap {
#XmlAnyElement(lax = true)
#XmlElementWrapper(name = "xmlData")
Object wrappedMD;
}
Then MDWrap can contain an object of any type, and will unmarshal properly, as long as the class of which wrappedMD is an instance of is annotated with #XmlRootElement. The trick is to annotate wrappedMD as XmlAnyElement.
I currently use JAXB for a project i'm working on and looking to convert my libraries archived xml to archived json, To act in my project. I figured I would use Jettison as it seems it would be easier to implement since it actually works with JAXB; however, looking at Older benchmarks in which Jettison was not included I have found Kryo produces smaller files and Serialized and DeSerialized quicker than some alternatives.
Can anyone inform me of the key difference or otherwise how Jettison stacks up to Kryo, especially for future projects such as android applications.
EDIT:
I guess i'm looking for what produces smaller files and operates faster. Human readability can be sacrificed since I don't plan on reading the files only processing them
They are for somewhat different purposes:
Jettison is for reading/writing JSON. Use it if you need to interoperte with a JSON (human-readable) data format
Kryo is for efficient binary serialisation. Use it if you need high performance and small encoded object sizes (e.g. communication of messages in a realtime game).
Since it sounds like you are using the format to archive data, human-readability and the use of a standard long-lived format is probably more important than efficiency, so I suspect you will want to choose the JSON route.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Since you already have established JAXB mappings and are converting XML to JSON, you may be interested in EclipseLink JAXB (MOXy) which offers both object-to-XML and object-to-JSON mapping using the same JAXB metadata.
Customer
Below is a sample model with JAXB annotations.
package forum11599191;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
#XmlAttribute
private int id;
private String firstName;
#XmlElement(nillable=true)
private String lastName;
private List<String> email;
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<customer id="123" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<firstName>Jane</firstName>
<lastName xsi:nil="true"/>
<email>jdoe#example.com</email>
</customer>
Demo
The following demo code will populate the objects from XML and then output JSON. Note how there are no compile time dependencies on MOXy.
package forum11599191;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
// Unmarshal from XML
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11599191/input.xml");
Customer customer = (Customer) unmarshaller.unmarshal(xml);
// Marshal to JSON
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.marshal(customer, System.out);
}
}
JSON Ouput
Below is the output from running the demo code.
{
"customer" : {
"id" : 123,
"firstName" : "Jane",
"lastName" : null,
"email" : [ "jdoe#example.com" ]
}
}
A few things to note about the output:
Since the id field is a numeric type it was marshalled to JSON without quotes.
Even though the id field was mapped with #XmlAttribute there are no special indication of this in the JSON message.
The email property had a List of size one, this is properly represented in the JSON output.
The xsi:nil mechanism was used to specify that the lastName field had a null value, this has been translated to the proper null representation in the JSON output.
For More Information
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html
http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html
I am looking to convert a class that looks like this ...
public class Amenity {
public String id;
public String value;
}
into the following XML using JaxB annotations:
<amenity id="id-string-here">value-string-here</amenity>
Does anyone know what annotation to use on the value member variable to accomplish this? The closest I've gotten so far is:
#XmlRootElement
public class Amenity {
#XmlAttribute
public String id;
#XmlElement
public String value;
}
Unfortunately this approach doesn't allow me to specify that the value member variable should not be rendered as its own tag <value></value>.
I'm not 100% sure about this, but try to use an #XmlValue annotation instead of #XmlElement.
It looks like the question was referring to text nodes not CDATA nodes, but here is a link on how EclipseLink JAXB (MOXy) handles CDATA:
http://bdoughan.blogspot.com/2010/07/cdata-cdata-run-run-data-run.html
This documentation writes:
Q. How can I cause the Marshaller to generate CDATA blocks?
A. This functionality is not available from JAXB directly, but you can configure an Apache Xerces-J XMLSerializer to produce CDATA blocks. Please review the JaxbCDATASample.java sample app for more detail.
(btw, this does not answer your particular question, but since the question title is misleading, and this is the first google result for jaxb CDATA, I'm answering a bit different question)
JAXB does not support marshaling/marshaling to/from CDATA xml types.