I have to use a lib that only gives me a JAXBContext to marshall and unmarshall XML data to Java objects. Also I don't ever see XML: Only the JAXB objects are passed to me. What I now need is a conversion of those objects not to XML, but to JSON.
Is there a way to create a marshaller from the given JAXBContext that can be used to generate JSON output?
The situation is that I'm not only transforming data. I also have logic that acts on the Java objects between XML and JSON (and manipulates the data). Also it's a two-way transformation. The JAXBContext is the information I have about the transformation between XML and Java objects - My intention was to reuse this context information for not having to implement a second transformation with a second technology different to JAXB. The JAXBContext (and its Java objects) already have the information about the XML structure; The automated recognition of that structure by JAXB is the time-saving reason for using it.
If your JAXB classes just use the basic annotations, you can take a look at JacksonJAXBAnnotations, allows Jackson mapper to recognize JAXN annotations. Four lines of code (in the simplest marshalling case) would be all you would need.
ObjectMapper mapper = new ObjectMapper();
JaxbAnnotationModule module = new JaxbAnnotationModule();
mapper.registerModule(module);
mapper.writeValue(System.out, yourJaxbObject);
You can see the link above for all the supported annotations. The maven artifact you'll need is
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>2.4.0</version>
</dependency>
See the github for jackson-module-jaxb-annotations - Note this artifact has dependencies on jackson-core and jackson-databind. So if you're not using maven, then you will need to make sure to download these artifacts also
Simple Exmaple:
JAXB Class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"hello",
"world"
})
#XmlRootElement(name = "root")
public class Root {
#XmlElement(required = true)
protected String hello;
#XmlElement(required = true)
protected String world;
// Getters and Setters
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<root>
<hello>JAXB</hello>
<world>Jackson</world>
</root>
Test
public class TestJaxbJackson {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream is = TestJaxbJackson.class.getResourceAsStream("test.xml");
Root root = (Root)unmarshaller.unmarshal(is);
System.out.println(root.getHello() + " " + root.getWorld());
ObjectMapper mapper = new ObjectMapper();
JaxbAnnotationModule module = new JaxbAnnotationModule();
mapper.registerModule(module);
mapper.writeValue(System.out, root);
}
}
Result
{"hello":"JAXB","world":"Jackson"}
Update
Also see this post. It looks like MOXy also offers this support.
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´m using ElasticSearch to store some data related to events that are happening with objects, a Track and Trace Model. To do that I used JAXB to generate de model classes using a XSD file.
I can save the data on ES easily transforming the Data data comes in XML to POJO and after that to JSON.
The problem that I´m having is to get the data back. I´m trying to use the same logic JSON to POJO (using JAXB) and POJO to XML on the web service. Like that:
JAXBContext jc = JAXBContext.newInstance(EPCISDocumentType.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", "application/json");
unmarshaller.setProperty("eclipselink.json.include-root", true);
String eventJSON = elasticEvent.getSpecific("event", "raw", eventID);
The String comes with the expected event but when I try to transform to POJO the object comes only with the outer most class (EPCISDocumentType) but with 0 contents. I´m trying like that:
StreamSource eventStream = new StreamSource(new StringReader(eventJSON));
EPCISDocumentType foundEvent = unmarshaller.unmarshal(eventStream,EPCISDocumentType.class).getValue();
The problem is why this is happening, I´m using exactly the same libraries to do Marshal but I can´t unmarshal it back to the same content.
I use ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper) for both marshaling and unmarshaling. It's much easier to use.
See bellow:
ObjectMapper mapper = new ObjectMapper();
//Get the json as String
String objectAsJsonString= mapper.writeValueAsString(foundEvent);
//Create the object from the String
EPCISDocumentType foundEvent = mapper.readValue(objectAsJsonString,EPCISDocumentType.class);
When you have lists of objects you want to marshal/unmarshal, it works only if you put the list in a wrapper.
Also jackson is more performant than jaxb.
Check these tests: http://tuhrig.de/jaxb-vs-gson-and-jackson/
I am using JAXB 2.0 for the Application Deveopment which is using RestFul Webservices .
Now there is a modification in the request , that is i will be getting another filed/variable in the request XML .
<Root Id="567" att="758" />
Modified Request will be
<Root Id="567" att="758" anotherfiledadded ="kiran" />
My question is , is it possible to automatically append that field (anotherfiledadded)in the UserData class (Without modifying the UserData ??)
The below is my UserData class
#XmlRootElement(name = "Root")
#XmlAccessorType(XmlAccessType.FIELD)
public class UserData {
#XmlAttribute
private String Id;
#XmlAttribute
private String att;
// getters and setters
You can try adding the field at runtime with javassist. But... It looks like you would also require to add the Annotation #XmlAttribute and I don't know if javassist allows you to add annotations... Anyways give it a try.
See: Javassist Add
You could use XSLT to apply an attribute into your XML document. All of the classes below are available in the JDK/JRE since Java SE 6.
JAXBContext jc = JAXBContext.newInstance(UserData.class);
JAXBSource source = new JAXBSource(jc, myUserData);
StreamResult result = new StreamResult(System.out);
TransformerFactory tf = TransformerFactory.newInstance();
StreamSource xslt = new StreamSource("addMyAttribute.xslt");
Transformer t = tf.newTransformer(xslt);
t.transform(source, result);
If you are implementing your RESTful service using JAX-RS you could plug-in this logic via a MessageBodyWriter:
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
Greetings! I have a server returning XML content to my client that looks like this:
<string xmlns="...">foobar</string>
I'm new to JAXB and have gotten a lot of stuff working, except this. I thought it would be pretty easy to marshal and unmarshal this to and from a String. It took a while, but I finally figured out how to marshal this as
public static String ToXML(String s) throws Exception
{
JAXBContext context = JAXBContext.newInstance(String.class);
Marshaller marshaller = context.createMarshaller();
StringWriter sw = new StringWriter();
marshaller.marshal(new JAXBElement(new QName("", "String"), String.class, s), sw);
return sw.toString();
}
So my question is, how to I unmarshal this? It cannot be annotated as a root element. I cannot use java.lang as the package to create a new instance of a JAXBContext (I get an ObjectFactory missing exception).
Any wisdom to impart? This can't be that hard, right?
You need to write an object model that conforms to your XML structure, and tell JAXB to unmarshal on to that. Your example may look simple, but it's not what JAXB is for.
Try something like this:
#XmlRootElement(name="string", namespace="blah")
public class MyString {
#XmlValue
String value;
}
JAXBContext context = JAXBContext.newInstance(MyString.class);
MyString myString = (MyString) context.createUnmarshaller().unmarshal(...);
This will unmarshal the XML <string xmlns="blah">foobar</string>. Change the namespace accordingly. If you have many namespaces, then JAXB isn't really the tool for you.
I was surprised by this, but the following seems to work:
final String xmlString = "<string>value</string>";
final StringReader xmlReader = new StringReader(xmlString);
final StreamSource xmlSource = new StreamSource(xmlReader);
final JAXBContext jaxbContext = JAXBContext.newInstance(String.class);
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
final String stringValue = unmarshaller.unmarshal(xmlSource, String.class).getValue();
Is that what you were looking for?
When you're using JAXB, you need to build the code around an XML schema. That is, if you have a file, say foo.xsd, you need to run it through the xjc compiler (in JDK 6 by default, otherwise you can download JAXB 2 and use that). That will read through the schema and generate the Java bean and associated ObjectFactory classes with the elements in the schema. The Java bean classes will look like regular POJOs with annotations. The ObjectFactory classes are needed by the JAXB implementation to convert the XML into the corresponding Java bean. This explains your ObjectFactory missing exception.
So it's not hard, but there is some leg work involved. We use it for one of our production applications and it's great. I see myself using it more now that it's part of the JDK.