JACKSON support for Java Generics?

I am currently working on a restFul project that is based on a schema. So, we use JAXB to convert XSD -> JAVA. I have a class as follows:

@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "systemResponse" }) @XmlRootElement(name = "RestResponse") public class RestResponse implements Serializable { private final static long serialVersionUID = 1L; @XmlElementRef(name = "SystemResponse", namespace = "http://www.intuit.com/psd/cdm/v1", type = JAXBElement.class) protected JAXBElement<? extends CdmComplexBase> systemResponse; ... } 

Here's how it gets serialized:

 {"systemResponse":{"name":"{http://www.intuit.com/psd/cdm/v1}Transactions","declaredType":"com.intuit.psd.cdm.v1.Transactions","scope":"javax.xml.bind.JAXBElement$GlobalScope","value":{"requestId":null,"requestName":null,"isEncrypted":null,"totalCount":null,"pageSize":null,"genDuration":null,"genDateTime":null,"transaction":[{"id":null,"externalKey":[],"metaData":null,"accountNumber":"12345678798","transactionNumber":null,"transactionReference":null,"batchID":null,"batchCycleDate":null,"paymentType":null,"paymentMethod":null,"transactionType":null,"cardType":null,"amount":null,"transactionDate":null,"authCode":null,"customerTransactionID":null,"ccnumberFirstSix":null,"ccnumberLastFour":null,"etctype":null,"posentryType":null}]},"nil":false,"globalScope":true,"typeSubstituted":false}} 

When I try to deserialize, I get the following exception:

 Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class javax.xml.bind.JAXBElement<com.intuit.psd.cdm.v1.CdmComplexBase>]: can not instantiate from JSON object (need to add/enable type information?) at [Source: java.io.StringReader@725d9aa7 ; line: 1, column: 20] (through reference chain: com.intuit.psd.cdm.v1.RestResponse["systemResponse"]) at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObjectUsingNonDefault(BeanDeserializer.java:400) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:289) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375) at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2796) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1942) at com.bp.samples.json.HelperJackson.testMarshalUnmarshal(HelperJackson.java:122) at com.bp.samples.json.JSONToJavaTest.main(JSONToJavaTest.java:42) 

Googling for solutions assumes that you need to either add more metadata to the serialized form, or register a deserializer. Jettison uses JAXB annotations for successful serialization and de-serialization. In addition, I can also remove the namespace "http://www.intuit.com/psd/cdm/v1" from the serialized one. Is there a way to do the same with JACKSON?

This is the code I use to configure JACKSON:

  ObjectMapper mapper = new ObjectMapper(); AnnotationIntrospector introspectorPrimary = new JacksonAnnotationIntrospector(); AnnotationIntrospector introspectorSecondary = new JaxbAnnotationIntrospector(); AnnotationIntrospector pair = new AnnotationIntrospector.Pair(introspectorPrimary, introspectorSecondary); mapper.getSerializationConfig().with(pair); mapper.getDeserializationConfig().with(pair); 
+4
source share
2 answers

Note. I have EclipseLink JAXB (MOXy) leads and a member of the JAXB Group (JSR-222) .

Jackson is not a JAXB (JSR-222) compatible implementation, it only supports a subset of JAXB annotations in its JSON binding implementation. For JAXB-generated models, you might be interested in EclipseLink JAXB (MOXy), which supports JSON binding.

JAVA MODEL

Below is a partial Java model that I suggested from your question.

Restresponse

 package forum13591952; import java.io.Serializable; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "", propOrder = { "systemResponse" }) @XmlRootElement(name = "RestResponse") public class RestResponse implements Serializable { private final static long serialVersionUID = 1L; @XmlElementRef(name = "SystemResponse", namespace = "http://www.intuit.com/psd/cdm/v1", type = JAXBElement.class) protected JAXBElement<? extends CdmComplexBase> systemResponse; } 

Cdmcompplexbase

The following is a simplified version of the CdmComplexBase class.

 package forum13591952; import javax.xml.bind.annotation.XmlSeeAlso; @XmlSeeAlso({Transactions.class}) public class CdmComplexBase { } 

Deals

The following is a simplified version of your Transactions class.

 package forum13591952; public class Transactions extends CdmComplexBase { private long accountNumber; public long getAccountNumber() { return accountNumber; } public void setAccountNumber(long accountNumber) { this.accountNumber = accountNumber; } } 

ObjectFactory

The following is a simplified version of your ObjectFactory class. It points out the @XmlElementDecl annotations that are used with your use of @XmlElementRef .

 package forum13591952; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; import javax.xml.namespace.QName; @XmlRegistry public class ObjectFactory { @XmlElementDecl(name="SystemResponse", namespace="http://www.intuit.com/psd/cdm/v1") public JAXBElement<CdmComplexBase> createCdmComplexBase(CdmComplexBase value) { return new JAXBElement<CdmComplexBase>(new QName("SystemResponse"), CdmComplexBase.class, value); } @XmlElementDecl(name="Transactions", namespace="http://www.intuit.com/psd/cdm/v1", substitutionHeadName="SystemResponse", substitutionHeadNamespace="http://www.intuit.com/psd/cdm/v1") public JAXBElement<Transactions> createTransactions(Transactions value) { return new JAXBElement<Transactions>(new QName("Transactions"), Transactions.class, value); } } 

jaxb.properties

To specify MOXy as the JAXB provider, you need to include a file named 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

The demo code below will marshal objects for both XML and JSON

 package forum13591952; import javax.xml.bind.*; import org.eclipse.persistence.jaxb.MarshallerProperties; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(RestResponse.class, ObjectFactory.class); ObjectFactory objectFactory = new ObjectFactory(); RestResponse response = new RestResponse(); Transactions transactions = new Transactions(); transactions.setAccountNumber(12345678798L); response.systemResponse = objectFactory.createTransactions(transactions); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // Marshal to XML marshaller.marshal(response, System.out); // Marshal to JSON marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json"); marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false); marshaller.marshal(response, System.out); } } 

OUTPUT

The following is the result of running the demo code. Note how the JSON representation is very similar to the XML representation.

 <?xml version="1.0" encoding="UTF-8"?> <RestResponse xmlns:ns0="http://www.intuit.com/psd/cdm/v1"> <ns0:Transactions> <accountNumber>12345678798</accountNumber> </ns0:Transactions> </RestResponse> { "Transactions" : { "accountNumber" : 12345678798 } } 

FOR FURTHER INFORMATION


UPDATE # 1

Below are answers to your first set of follow-up questions:

1) Is there a way to a stone case JSON variables: so AccountNumber → account number only for JSON?

You can use the external MOXy mapping file to customize the JSON representation. See the related answer below for a complete code example.

2) Does Jettison have a namespace mapping, does it also have Moxy?

By default, MOXy does not require you to emulate the namespace qualifications in your JSON message according to your XML structure. In our XML binding, we perform matching based on a qualified name and in our JSON binding, we do matching based on a local name based on the same metadata. We also support Jettison style namespaces. The related answer below contains a complete example of how this is done using MOXy.

3) What will be the advantage of Moxy over Jettison, I think performance?

Jettison is a library that converts JSON to / from StAX events so that it can be used with an XML binding library to create / use JSON (see http://blog.bdoughan.com/2011/04/jaxb-and-json -via-jettison.html ). Because of this, Jettison has problems with the following points, which MOXy does not.


UPDATE # 2

Below are answers to your second set of follow-up questions:

1) How to fix the Jettison list issue?

Jettison converts StAX events to / from JSON based solely on the events received. This means that to recognize the collection, you need to get 2 startElement events with the same name, so lists of size 1 are not represented as JSON arrays. Since MOXy provides built-in JSON binding, it knows when data comes from the list.

2) So, do you go from JAXB classes directly to JSON?

Yes, MOXy converts Java objects (with JAXB and MOXy annotations) directly to / from JSON.

3) Is there a paper or link that goes through question number 3 in more detail?

We do not have a document comparing MOXy with Jettison. We studied the pain that people experienced when using Jettison, and made sure that we eliminated them. Although MOXy does not need to emulate XML concepts such as attributes and namespaces in JSON such as Jettison, we provide settings to enable this behavior to make it easier for people to switch from Jettison to MOXy.

At this point, I would like to recommend MOXy for Intuit when I do JAXB + JSON.

Thank you for your support. I am pretty active on Stack Overflow, but if you post questions to the EclipseLink Forum , you can get support from the whole team.

+4
source

JAXBElement is an XML-specific data type (basically similar to the DOM tree), and Jackson does not know what to do with it. The reason Jettison can handle this is because it is based on an XML API and only converts to JSON on the output (or the very bottom of the input).

But why are you using JAXBElement? This is a reserve used for cases when there is no way to really bind data; sort of like mapping data to a Java Map or so. Could you use a real POJO? This will work fine.

+1
source

All Articles