Zoop
Zoop

Reputation: 656

Convert JSON string to "Object.class"

I am currently trying to use Jackson to turn an object into a JSON string this was easily done by

public byte[] toJSON(Object obj) throws IOException {
        ObjectMapper map = new ObjectMapper();
        return map.writeValueAsString(obj).getBytes();
    }

Where i run into trouble is when i was to take the array of bytes and turn them into an Object. Currently i have:

public Object toObject(byte[] bytes) throws IOException, ClassNotFoundException {
    ObjectMapper map = new ObjectMapper();
    return (Object)map.readValue(bytes, Object.class);
}

I successfully convert an object to a JSON string but the Object returned from the toObject method is always a LinkedHashMap instead of the object that was initially turned into the JSON string.

Sorry if i did a poor job communicating my problem but ill try to sum it up simply. I want my code to be able to do the following:

MyClass someObject = new MyClass();
String json = toJSON(someObject);
Object tempObject = toObject(json);
MyClass sameObject = (MyClass) tempObject;

this code currently throws the following:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.MyClass

Any help on the matter would be appreciated!

Upvotes: 0

Views: 7472

Answers (2)

StaxMan
StaxMan

Reputation: 116482

You are requesting an Object, so Jackson creates one Object it knows it can create: a Map. JSON array would become a List and so on.

If you want MyClass, you must request it explicitly, or, if you want a general-purpose serialization/deserialization, for inclusion of type identifier, as suggested by @csd.

Upvotes: 1

csd
csd

Reputation: 1784

First, I'm a little confused why you need to deal with byte[] at all. objectMapper.writeVaueAsString(obj) gives you a perfectly good String with the JSON representation of your object. If you need to convert to byte[] then you need to convert back to String before calling readValue.

But putting that aside for now, let's suppose you've got that String containing the JSON. By default, Jackson isn't smart enough to know (from looking at the JSON alone) what Java class to instantiate when you call readValue. In your example, you pass in Object.class, which isn't very helpful for Jackson since EVERY object in Java extends from Object. So in that case, it chooses its built-in default, which is to build a Map (in particular, a LinkedHashMap) with keys and values as listed in the JSON.

As one commenter pointed out, you can give Jackson a hint and pass MyClass.class as the second parameter to readValue (instead of Object.class). If you did that, then JSON will create an instance of MyClass at the beginning of readValue. And when it sees something like "foo: 43" in the JSON, it'll try to find a field or setter (i.e. public int foo; or public void setFoo(int f)) in MyClass and set the value accordingly.

If you don't know ahead of time what type of object it is (i.e. what value to pass as the second parameter to readValue) then there are other options. One of example of this is to require that your JSON objects have a class property that Jackson will use as a hint for which type of Java object to create. Here's what that code might look like:

// This annotation tells Jackson to include the class name as
// a "class" property when *writing* JSON.
@JsonTypeInfo(include=As.PROPERTY, use=Id.CLASS, property="class")
public class MyClass {
    public int foo;
}

This will make your JSON look like this:

{ "class":"MyClass", foo:0 }

Then in your code to read the object, do this:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTypingAsProperty(DefaultTyping.JAVA_LANG_OBJECT, "class");
// Note Object.class as second parameter.  Jackson will find the "class"
// property in the JSON and use it to determine which class to make.
MyClass foo = (MyClass) objectMapper.readValue(jsonString, Object.class);

Upvotes: 1

Related Questions