Java dealing with two custom deserializers - java

so this is a very weird situation that I came across and have no idea how to handle.
I have a service that contains logic to deserialize an object from a request sent to it. Now the object in question is being changed and a new deserialization method is implemented to deal with that (don't ask why, all I know is we just need to change the method of deserialization).
The problem is that this change needs to be backwards compatible so we should be able to deal with both types of objects. In order to do this, we need to be able to determine the correct deserializer to use depending on the type of object but how do we do that if the object is serialized into a byte buffer? Im out of ideas... Is there a different/better way of going about this change?
The service is in Java.
Edit 1: Clearing up my intentions .
The old object used a custom serializer and the new one uses an ObjectMapper JSON serializer. So my goal is to be able to detect if I am dealing with the old or new object so I can deserialize accordingly.
I can try to use the new deserializer and catch the JsonParseException it throws and in the catch block, use the old serializer but this is not the way I want to handle a JsonParseException.

Serializable classes should have a serialVersionUID that is static, final, and of type long.
This is to ensure that the class of the object that was serialized is same as the class of the object being deserialized.
In order to achieve backward compatibility follow these steps:
Ensure that whenever class structure is changed, you change the value of this field.
Use your new custom serializer to deserialize the object.
If the object is of the previous class you will get an InvalidClassException. Catch this exception and try to deserialize that object with the legacy deserializer inside catch block.
This ensures that your custom deserializer has backward compatability.

First of all, you need to identify a difference between the new and the old object. You'll use that to switch to the old de-serializer.
You'll also need a specific ObjectMapper for these two classes, old and new.
Create a Module, and register it
final SimpleModule module = new SimpleModule();
module.addDeserializer(Object.class, new NewDeserializer(new OldDeserializer()));
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
Prepare the new StdDeserializer, which will accept as constructor argument the old one.
public class NewDeserializer extends StdDeserializer<Object> {
private final StdDeserializer<Object> oldDeserializer;
NewDeserializer(final StdDeserializer<Object> oldDeserializer) {
super(NewObject.class);
this.oldDeserializer = oldDeserializer;
}
#Override
public Object deserialize(
final JsonParser parser,
final DeserializationContext context) throws IOException {
final ObjectCodec codec = parser.getCodec();
// Read the JSON document to a tree
final TreeNode treeNode = codec.readTree(parser);
// Identify if it is the new format, or the old one
final TreeNode newField = treeNode.get("newField");
if (newField == null) {
// Delegate to the old de-serializer
final JsonFactory factory = new JsonFactory(parser.getCodec());
final JsonParser oldParser = factory.createParser(treeNode.toString());
return oldDeserializer.deserialize(oldParser, context);
}
return codec.readValue(treeNode.traverse(), NewObject.class);
}
}
The old StdDeserializer
public class OldDeserializer extends StdDeserializer<Object> {
OldDeserializer() {
super(OldObject.class);
}
#Override
public Object deserialize(
final JsonParser parser,
final DeserializationContext context) throws IOException {
return parser.getCodec().readValue(parser, OldObject.class);
}
}
Now, simply call
objectMapper.readValue(v, Object.class);

Related

Deserialize json by class name

How i can deserialize my object from json string, if i have only string name of my class, by canonical name invoke, and json string representation of object?
In other words, i need method like this:
public <T> T deserialize(String json, String className) {
// some code
}
We have class name and json string - and we need to deserialize json to java object.
I know how to deserialize json to object if i know the class of object, for example, we can use Jackson:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Car car = objectMapper.readValue(json, Car.class);
But in my task there is a different classes - it may be Cat.class, Dog.class, Car.class and etc. And every class with different state. And i need universal method for deserializing.
I write something like:
public final class Utils implements Serializable {
private Utils () {
throw new AssertionError();
}
private static ObjectMapper objectMapper = new ObjectMapper();
public static String toJson(Object obj) {
String objectToJson;
try {
objectToJson = objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new UncheckedIOException(String.format("Can't serialize object %s to JSON string", obj), e);
}
return objectToJson;
}
public static <T> T fromJson(String json, String className) throws ClassNotFoundException, IOException {
Class<?> clz = Class.forName(className);
return (T) objectMapper.readValue(json, clz);
}
}
And it works with example of car above, but with a warning cast exception.
But maybe there is a better way?
Oh, and not all objects will be so simple. Some of them would encapsulate collections and other objects.
The problem stems from the compiler wanting to (virtually) make a different instance of this method for every type T that it is used on. However, there is nothing in the arguments to hang its hat on.
If the caller doesn't know what class to expect, other than a String representation of that class (which I'm assuming has been saved in the JSON as well?), what is he going to do with the result of this call? Since he doesn't know, he must be using it as simply Object, in which case the method might as well simply return Object.
If the caller does know the class, then he can pass in a Class as the argument, and you won't have any issues.
It occurred to me as writing this that ObectMapper must have a similar problem for some of its readValue methods, such as the one that accepts ResolvedType instead of Class. I notice that, in that class' source code, it includes /unchecked/ before the class. I"m not sure if that is to get around this issue or not, but it makes sense.

Gson, Deserialize Wrapper class

I need help with the gson library, basically i have to de-serialize a wrapper class defined like this:
static class WrapperClass {
public int value;
private final String otherData;
public WrapperClass(String otherData) {
this.otherData = otherData;
}
}
The class that I have to "jsonize" has fields like this:
private final WrapperClass wrappedData = new WrapperClass("other data");
The serializer of the wrapper class is really simple:
#Override
public JsonElement serialize(WrapperClass src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.value);
}
The problem is the de-serialization side, I should not re-instantiate the WrapperClass object because it's already there by default, i have only to de-serialize it's value, it's possible to this with the gson library?
So, what you want to do: "read" the content of some JSON string and "overlay" existing objects with that.
I am not aware of a built-in way to do that. Beyond that: doing something of this complexity under the covers sounds like the wrong approach, too.
Thus, my recommendation: clearly separate those responsibilities. Meaning: instead of of creating a (almost counter-intuitive) solution that merges JSON data "into" existing objects: do that explicitly, it step by step. Like:
read the JSON data and de-serialize into one or more objects
then have an another special component "update" your "old" objects with the information found in the de-serialized objects

Convert complex Java object to json with gson.toJsonTree

I need to convert a java object (called org) to json format.
The object (DTO ) is a bit complex, because it contains a list of objects of the same class and which in turn can also contain more objects of the same class ( built recursively). When I passing the object to gson.toJsonTree method it seems to fail (there isnt any error), but it seems that the method does not like complex objects). If I set to null the list of objects of the first object everything works fine. I can not modify the class, only the method that makes json.
JsonElement jsonUO = null;
jsonUO = gson.toJsonTree(org,OrgDTO.class);
jsonObject.add("ORG", jsonUO)
public class OrgDTO implements Serializable{
private String id;
......
private List sucesores;
public OrgDTO(){
this.sucesores = new ArrayList();
}
.....
}
It might be a little bit late for the questioner, however I share my answer in case someone else face similar issue:
You'll need to create a helper class that does the json serialization. It should implement the JsonDeserializer:
public class OrgDTOJsonSerializer implements JsonDeserializer<OrgDTO> {
#Override
public JsonElement serialize(OrgDTO src, Type type, JsonSerializationContext jsc) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", src.getId());
/// Build the array of sucesores (whatever it means!)
JsonArray sucesoresArray = new JsonArray();
for (final OrgDTO obj: src.getSucesores()) {
JsonObject succJsonObj = serialize(obj, type, jsc);
sucesoresArray.add(succJsonObj);
}
jsonObject.add("sucesores", sucesoresArray);
return jsonObject;
}
}
Then you'll need to register it in gson before attempting to serialize any object of that type:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(OrgDTO.class, new OrgDTOJsonSerializer());

An RPC mechanism which handles type hierarchy correctly in java

I need a mechanism for calling remote methods on server which handles type hierarchy correctly. For example GWT can do this, but my client isn't javascript! By handling type hierarchy, I mean transfering child objects even when the type is declared as a parent class! Suppose we have a class Container:
class Parent {
String name;
}
class Child extends Parent {
String family;
}
class Container {
Parent p;
}
And I have a method on server with the following signature:
void doSomethingImportant(Container c) {}
If I call this method on client, and pass an instance of Container, which has an instance of Child as property "p", I expect to get an instance of Child on server too (which would have "family" property)!
GWT handles this without any problem, is there any other technologies that can handle this?
I didn't find a RPC mechanism for this, but I managed to use JSON in order to handle this. I found Gson which is google's API for using JSON in java. It converts objects to JsonElements which can be interpretted as Strings and vice versa.
So the key feature that helped me develop this, was Gson's custom Serializer/Deserializer. I implemented a class which is Serializer and Deserializer for Object, and I send the class name for source class along the class's content:
class MySerializerAndDeserializer implements JsonSerializer<Object>, JsonDeserializer<Object>{
public JsonElement serialize(Object src, Type typeOfSrc,
JsonSerializationContext context) {
Class clazz = src.getClass();
JsonElement serialize = context.serialize(src);
JsonArray array = new JsonArray();
array.add(new JsonPrimitive(clazz.getName()));
array.add(serialize);
return array;
}
public Object deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonArray array = json.getAsJsonArray();
String asString = array.get(0).getAsString();
Object deserialize = null;
try {
deserialize = context.deserialize(array.get(1).getAsJsonObject(), Class.forName(asString));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return deserialize;
}
}
and then I registered MySerializerAndDeserializer for Parent.class:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Parent.class, new MySerializerAndDeserializer());
And finally used gson and got the Instance I expected correctly:
String json = gson.toJson(container, Container.class);
Container containerFromJson = gson.fromJson(json, Container.class);
Using sfnrpc (http://code.google.com/p/sfnrpc) you can specify which type must be used optionally in the class arguments.
Hope this helps.
Object [] objArr = new Object[1];
TestObject to = new TestObject();
to.data = "HELLO,";
objArr[0] = to;
**Class [] classArr = new Class[1];
classArr[0] = TestObject.class;**
TestObject response = (TestObject)rpcc.invoke("#URN1","127.0.0.1:6878","echo",true,60,"", objArr,classArr);
sl.debug("The response was:"+response.data);

Custom serialization with Ehcache

Is it possible to tell Ehcache to use a custom serialization when moving an object from the memory to the disc or off-heap cache and the other way? I want to skip some fields from serialization which are not declared transient (third-party-library) but which I do not need to store since I can easily recalculate them. I want to do this to save quite some memory. At best, I want to use a library like Kryo.
I found a solution by using wrappers and by adding another abstraction layer, a SerializationWrapper. The wrapper presented here uses Kryo:
final class SerializationWrapper implements Serializable {
private final static Kryo KRYO = new Kryo();
private Object object;
public SerializationWrapper(Object object) {
this.object = object;
}
private void readObject(ObjectInputStream objectInputStream)
throws IOException, ClassNotFoundException {
Input input = new Input(objectInputStream);
object = KRYO.readClassAndObject(input);
input.close();
}
private void writeObject(ObjectOutputStream objectOutputStream)
throws IOException {
Output output = new Output(objectOutputStream);
KRYO.writeClassAndObject(output, object);
output.close();
}
public Object getWrappedObject() {
return object;
}
}
The serialization methods readObject and writeObjectwill be called by the Java contract and allow me to implement a custom serialization. Additionally, This solution does not create a size overhead since I only write the wrapped object to the output stream and skip the writing of the wrapper entirely. You can also read this documentation for more info what helped me a lot.

Categories

Resources