Complex object (de)serialization into JSON using GSON - java

I have a problem with serializing and deserializing my object structure with GSON. In order to describe the problem i'll have to describe my class structure a bit:
I have a java abstract class, let's name it "A". There are also classes "BA", "CA", "DA" that are abstract too and they extend class "A". Each of them has it's own constructor, non of which is non-arg. Finally there are several (many!) classes those extend "BC", or "CA" or "DA". Instances of those "bottom" classes are kept in "ArrayList" list.
Now, i'm trying to "jsonize" that array list. For creating Json string I'm using this code:
Gson gs = new Gson();
Type listOfTestObject = new TypeToken<List<A>>(){}.getType();
String rez = gs.toJson(getListOfAs(), listOfTestObject);
And i'm trying to deserialize that json using this (in another class):
Type listOfTestObject = new TypeToken<ArrayList<A>>(){}.getType();
ArrayList<A> listOfAs = gs.fromJson(jsonREZString, listOfTestObject);
but above code throws this:
Unable to invoke no-args constructor for class packagename.A. Register an InstanceCreator with Gson for this type may fix this problem.
Now, I have create a non-args constructor in the class "A", but no luck. I have read about "InstanceCreator" but it looks like I would have to create an "InstanceCreator" for each concrete class that extends "A"! Right? I can't do it, because I have many (many!) classes that extend "A" through "BA", "CA" or "DA".
What am I missing? How can I simply deserialize (serialization seems fine) this complex structure without adding custom deserialializastion code for each type?

In fact you might have here 2 distinct problems.
1) You have polymorphic types thus you probably want to serialize objects as their concrete type and not A.
2) You want to deserialize to the concrete types that do not provide no arg ctrs.
Gson does not support 1 & 2, there is an extension for 1 but I never used it.
Maybe Genson solves your problem, it supports both 1 & 2.
Genson genson = new Genson.Builder()
// enables polymorphic types support
.setWithClassMetadata(true)
// enables no arg support
.setWithDebugInfoPropertyNameResolver(true)
.create();
// will look like: [{"#class": "com.xxx.SomeConcreteClass", ...}, {"#class": "com.XXX.OtherClass"}]
String json = genson.serialize(getListOfAs());
List<A> listOfA = genson.deserialize(json, new GenericType<List<A>>() {});
You don't need to specify the type during serialization, except if you want that only parent fields be present in the output.
ex: genson.serialize(getListOfAs(), GenericType>() {}) will serialize only attributes from A, you can also force genson to always use runtime types by setting setUseRuntimeTypeForSerialization(true) on the builder.
Also if you don't want impl details to leak in the json representation, you can define aliases (builder.addAlias("someAlias", SomeClass.class) for your types, they will be used instead of full package+classname.

Related

MapStruct: Object.class to Custom.class mapping

I'm a newbie with MapStruct, and need some help with that.
I have a Source class, with an attribute
Object input;
Which, in runtime, returns a custom object named TicketDetails.
Now, in my target class there is a attribute named,
MyTicket myTicket;
which, I need to map with an attribute of TicketDetails object.
For, better understanding, I'm writing the normal java code example below.
SourceClassModel sourceClassModel = new SourceClassModel();
TargetClassModel targetClassModel = new TargetClassModel();
//mapping
TicketDetails ticketDetails = (TicketDetails) sourceClassModel.getInput();
targetClassModel.setMyTicket(ticketDetails.getMyTicket);
Now, my question is, how to achieve this case using MapStruct?
Either on a used mapper (see #Mapper#uses()) or in a non-abstract method on the mapper itself - in case it is an abstract class and not an interface - define the mapping from Object to TicketDetails yourself:
TicketDetails asTicketDetails(Object details) {
return (TicketDetails) details;
}
The generated method for the conversion of SourceClassModel to TargetClassModel will then invoke that manually written method for converting the myTicket property.

MessagePack and Subclasses deserialization

I am playing around with MessagePack and Java. I have had experience with Protobuf and Json (using Jackson and Gson) in the past when it comes to serialization tools.
When it comes to normal serialization and deserialization I have no problems at all. It is when I want to have multiple subclasses of another class that a problem arise.
I am testing this with the following code :
TestMessage.TestMessageSubClass sub = new TestMessage.TestMessageSubClass();
byte[] pack = MsgPack.pack(sub);
Assert.assertTrue(ArrayUtils.isNotEmpty(pack));
List<? extends TestMessage> msg = MsgPack.unpack(pack, TestMessage.class);
Assert.assertNotNull(msg);
Assert.assertFalse(msg.isEmpty());
TestMessage temp = msg.get(0);
Assert.assertNotNull(temp);
Assert.assertTrue(temp instanceof TestMessage.TestMessageSubClass);
TestMessage.TestMessageSubClass sub2 = (TestMessage.TestMessageSubClass) temp;
Assert.assertEquals(sub, sub2);
System.out.println(sub);
System.out.println(sub2);
Those two lines fail because when I deserialize I only get a normal TestMessage, and not a TestMessageSubClass instance.
Assert.assertTrue(temp instanceof TestMessage.TestMessageSubClass);
TestMessage.TestMessageSubClass sub2 = (TestMessage.TestMessageSubClass) temp;
I suppose that this happens because by default the MessagePack unpacker has no way of determining the exact class of he needs to deserialize. In fact this would work just fine if I directly ask him to deserialize into a TestMessageSubClass.
My requirements is that TestMessage might have any number of subclasses with extra data, and with the same code I need to de-serialize them in the right class instance without losing anything. I might be deserializing a stream containing an heterogeneous list of those TestMessage instances.
I can have the behaviour I want using the #JsonSubTypes annotation in JacksonJson.
Is there a way to use the official MessagePack client API and obtain that? Is there a known pattern to do that myself?
Here is the code of my MsgPack wrapper class: GIST
Any advice on using MessagePack more efficiently is welcome too.

Creating mixins with CGLIB that implement a new interface

First off, I don't think this is necessarily a good idea, I'm just seeing if this is really possible. I could see some benefits, such as not having to explicitly convert to objects that we're sending to the client and using an interface to blacklist certain fields that are security concerns. I'm definitely not stuck on the idea, but I'd like to give it a try.
We're using Spring MVC + Jackson to generate JSON directly from objects. We have our domain object that contains necessary data to send to the client and we have a list of error strings that are added to every outgoing JSON request as needed.
So the return JSON might be something like
{ name: 'woohoo', location : 'wahoo', errors : ['foo'] }
Currently, we have a class that models what should be on the client side, but we always extend a common base class with the error methods.
So, we have:
interface NameAndLoc {
String getName();
String getLocation();
}
and
interface ResponseErrors {
List<String> getErrors();
void appendError(String);
}
We have two classes that implement these interfaces and would like to have CGLIB generate a new class the implements:
interface NameAndLocResponse extends NameAndLoc, ResponseErrors {}
Presently, with CGLIB mixins, I can generate an object with the following:
Object mish = Mixin.create(
new Class [] {NameAndLoc.class, ResponseErrors.class},
new Object [] { new NameAndLocImpl(), new ResponseErrorsImpl() } );
I could then cast the object to either NameAndLoc or ResponseErrors, however, what I would like to do is create an object that uses the same backing classes, but implements the NameAndLocResponse interface, without having to extend our common error handling class and then implement NameAndLoc.
If I attempt to cast with what I have, it errors out. I'm sure this is possible.
I think it is very similar to this, but not quite: http://www.jroller.com/melix/entry/alternative_to_delegate_pattern_with
Simply add the NameAndLocResponse interface to the Class array in the Mixin constructor as the last argument. The resulting object will implement it. You can find an example of this in this blog entry: http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html

Generics and JSON

I am using the Play framework.
I want to use the function renderJSON with 2 Objects as an argument. It doesn't seem to be possible so I am trying to create a class that contains the 2 objects. In order not to have to create a new class everytime, I want to use Generics but it doesn't seem to work:
Model :
public class JSONContainer<T> extends Model {
private T myT;
private StatusMessage mySm;
public JSONContainer(T myT, StatusMessage mySm) {
this.myT = myT;
this.mySm = mySm;
}
}
and then :
In a function of a Controller:
JSONContainer<User> myJ = new JSONContainer(logged,sm);
renderJSON(myJ);
where logged is a User, sm is a StatusMessage. I get the error:
type: 'play.exceptions.JavaExecutionException'
If I don't use Generics, it works fine. Any idea?
The console gives this output, where the line 43 is:
JSONContainer<User> myJ = new JSONContainer(logged,sm);
Generic entities can't be mapped by Hibernate.
You should do the generic class abstract and create specific implementations (using User and any other possible values of T). This should solve the issue
Instead of using a JSONContainer like I did, I think the best way is to go with Collection as shown in this user guide for GSON made by Google (the JSON mapper used by Play apparently) at http://sites.google.com/site/gson/gson-user-guide#TOC-Collections-Examples:
Collection collection = new ArrayList();
collection.add(logged);
collection.add(sm);
renderJSON(collection);
Collection are good for serializing (Java object to JSON) but not good for deserializing (see the GSON user guide for more insight).
It is better, IMHO, to use Collection than JSONContainer as JSONContainer are not useful in that case and don't give more possibilities.

Spring/json: Convert a typed collection like List<MyPojo>

I'm trying to marshal a list: List<Pojo> objects via the Spring Rest Template.
I can pass along simple Pojo objects, but I can't find any documentation that describes how to send a List<Pojo> objects.
Spring is using Jackson JSON to implement the HttpMessageConverter. The jackson documentation covers this:
In addition to binding to POJOs and
"simple" types, there is one
additional variant: that of binding to
generic (typed) containers. This case
requires special handling due to
so-called Type Erasure (used by Java
to implement generics in somewhat
backwards compatible way), which
prevents you from using something like
Collection<String>.class (which does
not compile).
So if you want to bind data into a
Map<String,User> you will need to use:
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() {});
where TypeReference is only needed to
pass generic type definition (via
anynomous inner class in this case):
the important part is
<Map<String,User>> which defines type
to bind to.
Can this be accomplished in the Spring template? I took a glance at the code and it makes me thing not, but maybe I just don't know some trick.
Solution
The ultimate solution, thanks to the helpful answers below, was to not send a List, but rather send a single object which simply extends a List, such as: class PojoList extends ArrayList<Pojo>. Spring can successfully marshal this Object, and it accomplishes the same thing as sending a List<Pojo>, though it be a little less clean of a solution. I also posted a JIRA in spring for them to address this shortcoming in their HttpMessageConverter interface.
In Spring 3.2 there is now support for generic types using the new exchange()-methods on the RestTemplate:
ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);
Works like a charm!
One way to ensure that generic type parameters are included is to actually sub-class List or Map type, such that you have something like:
static class MyStringList extends ArrayList<String> { }
and return instance of that list.
So why does this make a difference? Because generic type information is retained in just a couple of places: method and field declarations, and super type declarations. So whereas "raw" List does NOT include any runtime type information, class definition of "MyStringList" does, through its supertype declarations.
Note that assignments to seemingly typed variables do not help: it just creates more compile-time syntactic sugar: real type information is only passed with Class instances (or lib-provided extensions thereof, like JavaType and TypeReference in Jackson's case).
Other than this, you would need to figure out how to pass Jackson either JavaType or TypeReference to accompany value.
If I read the docs for MappingJacksonHttpMessageConverter right, you will have to create and register a subclass of MappingJacksonHttpMessageConverter and override the getJavaType(Class<?>) method:
Returns the Jackson JavaType for the
specific class. Default implementation
returns
TypeFactory.type(java.lang.reflect.Type),
but this can be overridden in
subclasses, to allow for custom
generic collection handling. For
instance:
protected JavaType getJavaType(Class<?> clazz) {
if (List.class.isAssignableFrom(clazz)) {
return TypeFactory.collectionType(ArrayList.class, MyBean.class);
} else {
return super.getJavaType(clazz);
}
}
I have solved this problem by using the following configuration:
private static final String POJO_ARRAY_LIST = PojoArrayList.class.getCanonicalName();
#Bean
public HttpMessageConverter<Object> httpMessageConverter() {
HttpMessageConverter<Object> httpMessageConverter = new MappingJackson2HttpMessageConverter() {
#Override
protected JavaType getJavaType(Type type, #Nullable Class<?> contextClass) {
JavaType javaType;
if (type != null && POJO_ARRAY_LIST.equals(type.getTypeName())) {
ObjectMapper objectMapper = new ObjectMapper();
TypeFactory typeFactory = objectMapper.getTypeFactory();
CollectionType collectionType = typeFactory.constructCollectionType(ArrayList.class, Pojo.class);
javaType = collectionType;
} else {
javaType = super.getJavaType(type, contextClass);
}
return javaType;
}
};
return httpMessageConverter;
}
where PojoArrayList is a final class that extends ArrayList<Pojo>.

Categories

Resources