The JSON is:
{"list": [1,2,3,4,5,6,7,8,9,10]}
Here's how I implement the JAXB bean:
package com.anon.sortweb.jaxb;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
#XmlRootElement
public class JsonBean {
#XmlElement(name="list")
private int[] list;
public JsonBean() {}
public void setList(int[] list) {
this.list = list;
}
public int[] getList() {
return list;
}
}
My web application works fine (I'm able to successfully access other resources) but this one resource (that I pass my JSON to) returns a 415 Media Type Unsupported exception.
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces("text/html")
public String sortAndReturnHtml(JsonBean listBean) { ... }
How do I correctly write my JAXB bean to encapsulate a list of integers?
Thanks in advance!
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Your JAXB bean is a perfectly reasonable representation of your JSON data. The JAXB (JSR-222) spec does not cover JSON binding so the answer ultimately comes down to how/if your JAX-RS implementation interprets JAXB metadata to produce/consume JSON.
Demo
Below is how it would work with MOXy.
package forum13648734;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {JsonBean.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum13648734/input.json");
JsonBean jsonBean = unmarshaller.unmarshal(json, JsonBean.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(jsonBean, System.out);
}
}
input.json/Output
{"list":[1,2,3,4,5,6,7,8,9,10]}
For More Information
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
Related
In the below format my doubt is the type mentioned with every field. Can you please suggest some solution? This is a requirement from the third party who will be consuming this.
subject":{
"type":"string",
"$":"Cabinet model number?"
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below is how this can be done using MOXy's JSON-binding.
Domain Model (Root)
The #XmlElement annotation can be used to specify the type of the property. Setting the type to be Object will force a type qualified to be written out.
import javax.xml.bind.annotation.*;
public class Root {
private String subject;
#XmlElement(type=Object.class)
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
}
Demo
Since a type qualifier will be marshalled out a key will need to be written for the value. By default this will be value. You can use the JSON_VALUE_WRAPPER property to change this to $.
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(3);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
properties.put(JAXBContextProperties.JSON_VALUE_WRAPPER, "$");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Root.class}, properties);
Root root = new Root();
root.setSubject("Cabinet model number?");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code.
{
"subject" : {
"type" : "string",
"$" : "Cabinet model number?"
}
}
For More Information
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
I have done this using gson API from google. Wrote a custom serializer which checks the type and value and creates the JSON object based on that.
I am writing a code to set XmlTransient at runtime using MOXy. Here is the part of the code which is adapted from the example on http://blog.bdoughan.com/2011/06/moxy-extensible-models-refresh-example.html
public void setXmlTransient(Class<?> domainClass, String propertyName) {
XmlTransient xmlTransient = new XmlTransient();
xmlTransient.setJavaAttribute(propertyName);
JavaType javaType = getJavaType(domainClass);
javaType.getJavaAttributes().getJavaAttribute().add(objectFactory.createXmlTransient(xmlTransient));
}
Since I am doing this programmatically, I need to be able to create the propertyName exactly the same way as MOXy does. For most getter method names, like getOrder, the property name is done by removing get from the method name and change upper-case O to lower-case o, i.e. property name is order. However, I am hitting the case which my getter method is getXInA but xInA doesn't seem to be a valid property name. MOXy throws a warning like
Ignoring attribute [xInA] on class [Atom] as no Property was generated for it.
Does anyone know what the rules are used by MOXy for creating the property name from getters? or know where I can find out about this without reading the MOXy source code?
SHORT ANSWER
Because there are two capital letters in a row the property name is going to be XInA.
LONG ANSWER
Domain Model (Foo)
Below is a sample Java class with the property from your question.
package forum14945664;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Foo {
private String x;
public String getXInA() {
return x;
}
public void setXInA(String x) {
this.x = x;
}
}
MetadataSource (ExampleMetadataSource)
MetadataSource is a programmatic way to provide MOXy with the mapping metadata.
package forum14945664;
import java.util.*;
import org.eclipse.persistence.jaxb.metadata.MetadataSourceAdapter;
import org.eclipse.persistence.jaxb.xmlmodel.*;
import org.eclipse.persistence.jaxb.xmlmodel.JavaType.*;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings.*;
public class ExampleMetadataSource extends MetadataSourceAdapter {
private ObjectFactory objectFactory;
private Map<Class<?>, JavaType> javaTypes;
private XmlBindings xmlBindings;
public ExampleMetadataSource() {
objectFactory = new ObjectFactory();
javaTypes = new HashMap<Class<?>, JavaType>();
xmlBindings = new XmlBindings();
xmlBindings.setPackageName("forum14945664");
xmlBindings.setJavaTypes(new JavaTypes());
}
#Override
public XmlBindings getXmlBindings(Map<String, ?> properties, ClassLoader classLoader) {
return xmlBindings;
}
public JavaType getJavaType(Class<?> clazz) {
JavaType javaType = javaTypes.get(clazz);
if(null == javaType) {
javaType = new JavaType();
javaType.setName(clazz.getSimpleName());
javaType.setJavaAttributes(new JavaAttributes());
xmlBindings.getJavaTypes().getJavaType().add(javaType);
javaTypes.put(clazz, javaType);
}
return javaType;
}
public void setXmlTransient(Class<?> domainClass, String propertyName) {
XmlTransient xmlTransient = new XmlTransient();
xmlTransient.setJavaAttribute(propertyName);
JavaType javaType = getJavaType(domainClass);
javaType.getJavaAttributes().getJavaAttribute().add(objectFactory.createXmlTransient(xmlTransient));
}
}
Specify MOXy as JAXB Provider (jaxb.properties)
To specify MOXy as the JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
In the demo code below we will create a JAXBContext based on the domain model and we will marshal an instance to XML. Then we will use the MetadataSource to make the property transient, refresh the JAXBContext and marshal the instance again.
package forum14945664;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.jaxb.JAXBHelper;
public class Demo {
public static void main(String[] args) throws Exception {
ExampleMetadataSource metadata = new ExampleMetadataSource();
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, metadata);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Foo foo = new Foo();
foo.setXInA("Hello World");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
metadata.setXmlTransient(Foo.class, "XInA");
JAXBHelper.getJAXBContext(jc).refreshMetadata();
marshaller.marshal(foo, System.out);
}
}
Output
First we see the XInA property marshalled, then after we make it transient we see that it is not in the XML from the second marshal operation.
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<XInA>Hello World</XInA>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo/>
I'm new both to RestEasy and JAXB. I think they're pretty easy to use as far as you control all the sources you want to expose via a web service.
But now I have a problem. I have data transfer objects that I cannot (should not) annotate with JAXB annotations but I still want them to marshal to XML.
What is the easiest way or the best practice to do so?
Any help or comment is appreciated.
Balázs
I had the same problem: I got fetched entity objects (already with real data) from the persistence layer, but they were from 3rd party classes, which I couldn't annotate with #XmlRootElement, nor change the fetching code.
To me, simply wrapping them in JAXBElement did the trick. So, the RESTful method:
#GET
#Path("/listAll")
#Produces(MediaType.APPLICATION_XML); // "application/xml"
public List<Person> getPersonList() {
return persistenceLayer.fetchAllPerson();
}
Worked when changed to:
#GET
#Path("/listAll")
#Produces(MediaType.APPLICATION_XML); // "application/xml"
public List<JAXBElement<Person>> getPersonList() {
List<Person> ps = persistenceLayer.fetchAllPerson();
List<JAXBElement<Person>> jaxbeps = new ArrayList<JAXBElement<Person>>(ps.size());
for (Person p : ps) {
jaxbeps.add(jaxbeWrapp(p));
}
return jaxbeps;
}
and the generic method used (you can just inline it, of course):
public static <T> JAXBElement<T> jaxbeWrapp(T obj) {
Class<T> clazz = (Class<T>) obj.getClass();
return new JAXBElement<T>(new QName(obj.getClass().getName().toLowerCase()), clazz, obj);
}
That's it! Hope it helps!
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
You could use MOXy's mapping document JAXB extension to provide the metadata. Then you could use a ContextResolver in JAX-RS to bootstrap the JAXBContext.
package blog.bindingfile.jaxrs;
import java.io.*;
import java.util.*;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import blog.bindingfile.Customer;
#Provider
#Produces({"application/xml", "application/json"})
public class CustomerContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext jc;
public CustomerContextResolver() {
ClassLoader cl = Customer.class.getClassLoader();
Map<String, Object> props = new HashMap<String, Object>(1);
props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "blog/bindingfile/binding.xml");
jc = JAXBContext.newInstance(new Class[] {Customer.class} , props);
}
public JAXBContext getContext(Class<?> clazz) {
if(Customer.class == clazz) {
return jc;
}
return null;
}
}
For a Detailed Example
http://blog.bdoughan.com/2011/04/moxys-xml-metadata-in-jax-rs-service.html
I want to add some attributes to Xml Elements using JAXB when marshalling from JavaBeans. The Xml Elements are simple data types like String. So I do not want to create new Classes. For example, a desired output would be:
<notifications>
<date>04/20/2011</date>
<subject creditcard_num="22678" checknum="8904">Credit Card Charge Back</subject>
<body payment_amount="34.00" return_status="charged back">some text</body>
</notifications
I do not want to define subject and body as separate classes.
-Anand
My solution require defining a class for subject and body, but the desired output will be as requested
I use #XmlValue for the message and #XmlAttribute for the attributes
#Test
public void testAll() throws JAXBException
{
String msg = "<notifications><date>04/20/2011</date><subject creditcard_num='22678' checknum='8904'>Credit Card Charge Back</subject><body payment_amount='34.00' return_status='charged back'>some text</body></notifications>";
Notifications tested = (Notifications) JAXBContext.newInstance(Notifications.class).createUnmarshaller().unmarshal(new StringReader(msg));
assertEquals("Credit Card Charge Back",tested.subject.value);
assertEquals("8904",tested.subject.checknum);
assertEquals("22678",tested.subject.creditcard_num);
}
#XmlRootElement
public static class Notifications{
public String date;
public Subject subject;
}
public static class Subject
{
#XmlValue
public String value;
#XmlAttribute(name="creditcard_num")
public String creditcard_num;
#XmlAttribute(name="checknum")
public String checknum;
}
NOTE:I only wrote the subject part, I wonder if using #XmlPath can be used to remove the need for different classes
You could use EclipseLink JAXB (MOXy)'s #XmlPath annotation to solve this problem (I'm the MOXy tech lead):
Notifications
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
#XmlAccessorType(XmlAccessType.FIELD)
public class Notifications {
private String date;
#XmlPath("subject/#creditcard_num")
private String creditcardNum;
#XmlPath("subject/#checknum")
private String checknum;
private String subject;
#XmlPath("body/#payment_amount")
private String paymentAmount;
#XmlPath("body/#return_status")
private String returnStatus;
private String body;
}
jaxb.properties
To use MOXy as your JAXB implementation you need to put a file named jaxb.properties in the same package as your model classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
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(Notifications.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Notifications notifications = (Notifications) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(notifications, System.out);
}
}
For More Information:
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
http://bdoughan.blogspot.com/2011/03/map-to-element-based-on-attribute-value.html
I'm trying to force brackets on lists that contain only one element.
I want something like this:
{"id":"0","industries":[{"id":"0","name":"Technologies"}],"name":"Google Inc."}
But I get:
{"id":"0","industries":{"id":"0","name":"Technologies"},"name":"Google Inc."}
Here is my Entity:
#Entity
#XmlRootElement
public class Company {
private int id;
private String name;
private String description;
#XMLElement(name="industries")
private List<Industry> industryList;
[...]
And finally, my JAXB Context Resolver:
public JAXBContextResolver() throws Exception {
MappedBuilder builder = JSONConfiguration.mapped();
builder.arrays("industries");
builder.rootUnwrapping(true);
this.context = new JSONJAXBContext(builder.build(), Company.class);
}
thanks for your help, but I found the answer. You actually need to specify a JAXBContextResolver which specify natural JSON configuration. You need to provide a type list of every container that need to be transform to JSON. In this example, you can see that I specified the GetCompanyResponse that is a container of Company.
#Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = { GetCompanyResponse.class };
public JAXBContextResolver() throws Exception {
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
}
public JAXBContext getContext(Class<?> objectType) {
for (Class clazz : types) {
if (clazz.equals(objectType)) {
return context;
}
}
return null;
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
EclipseLink JAXB (MOXy) provides native JSON-binding support. It will correctly marshal collections of size 1 wrapped as a JSON array. Below is a complete example.
Company
package forum3946102;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Company {
private int id;
private String name;
private String description;
#XmlElement(name = "industries")
private List<Industry> industryList;
}
Industry
package forum3946102;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Industry {
private int id;
private String name;
}
jaxb.properties
In order to specify MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum3946102;
import java.io.StringReader;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Company.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty("eclipselink.media-type", "application/json");
unmarshaller.setProperty("eclipselink.json.include-root", false);
String jsonString = "{\"id\":\"0\",\"industries\":[{\"id\":\"0\",\"name\":\"Technologies\"}],\"name\":\"Google Inc.\"}";
StreamSource jsonSource = new StreamSource(new StringReader(jsonString));
Company company = unmarshaller.unmarshal(jsonSource, Company.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.setProperty("eclipselink.json.include-root", false);
marshaller.marshal(company, System.out);
}
}
Output
Below is the output from running the demo code:
{"id" : 0, "name" : "Google Inc.", "industries" : [{"id" : 0, "name" : "Technologies"}]}
For More Information
JSON Binding with EclipseLink MOXy - Twitter Example
MOXy as Your JAX-RS JSON Provider - Server Side
I'm not too sure about this, but try removing the #XMLElement annotation for industryList
I have done stuff the other way around: using jaxb to generate java classes from xsd schema files. I've looked at the generated classes with collection fields, and they don't have any specific annotations on them.
Also you may wanna try JSON Lib: http://json-lib.sourceforge.net/
you could do things like:
jsonString = JSONObject.fromObject(pojoObject)
which will generate json string that will incorporate e.g. collections of complex types.
You could then send the jsonString using e.g. HttpServletResponse.
I would recommend serializing DTO objects rather than serialize your entity objects.
Drill down JSONObject and swap out an area that needs to be JSONArray. Once created, json putOpt will replace old reference object with new.
Before
"Investment": {"SubAccount": {
"id": "SubAccountId_2_CORP",
"AllocPercent": "100.0",
"ProductCode": "PC01",
"ProductFullName": "Product Full Name"
}}
After
"Investment": {"SubAccount": [{
"id": "SubAccountId_2_CORP",
"AllocPercent": "100.0",
"ProductCode": "PC01",
"ProductFullName": "Product Full Name"
}]}
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.XML;
private static int PRETTY_PRINT_INDENT_FACTOR = 4;
.
.
.
.
try {
org.json.JSONObject xmlJSONObj = XML.toJSONObject(inBatchTrans.getINPUT_MESSAGE());
try {
JSONArray subAcctArray = xmlJSONObj.getJSONObject("TXLife").getJSONObject("TXLifeRequest").getJSONObject("OLifE").getJSONObject("Holding").getJSONObject("Investment").getJSONArray("SubAccount");
// Already JsonArray do nothing
} catch (Exception e) {
// convert to Array
JSONObject subAcctObj = xmlJSONObj.getJSONObject("TXLife").getJSONObject("TXLifeRequest").getJSONObject("OLifE").getJSONObject("Holding").getJSONObject("Investment").getJSONObject("SubAccount");
JSONArray keys = subAcctObj.names();
JSONArray values = subAcctObj.toJSONArray(keys);
JSONObject subAccount = new JSONObject();
JSONArray subAccountList = new JSONArray();
int key = keys.length();
for (int i = 0; i < key; i++) {
subAccount.put(keys.get(i).toString(), values.get(i));
}
subAccountList.put(0, subAccount);
xmlJSONObj.getJSONObject("TXLife").getJSONObject("TXLifeRequest").getJSONObject("OLifE").getJSONObject("Holding").getJSONObject("Investment").putOpt("SubAccount", subAccountList);
}
String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
System.out.println(jsonPrettyPrintString);
} catch (org.json.JSONException je) {
System.out.println(je.toString());
}