v1shnu
v1shnu

Reputation: 2231

Determining type of Object from JSON and create object using GSON

I use Gson in my application where I have a couple of Java objects which I present to the front end by converting it to JSON using GSON. I know how to convert the JSON back to the Java object using gson.

I have this use case: When I supply a particular JSON string to GSON, I want it to find the type of the object from which the JSON string was obtained and the give me an object of that type.

Using GSON, I can get the object back only if I pass the Object class in the fromJson method. But is there anyway I can make gson to determine it on its own ? I have no control over the objects generated on the other side.

Example:

Person person = new Person();
SomeRandomObject obj = new SomeRandomObject();

String personJson = gson.toJson(person);
String anotherJson = gson.toJson(obj);

Now if I want to convert it back to the object, I use this:

Person p = gson.fromJson(personJson,Person.class);

But is there anyway that I can get the object like this :

Object object = gson.fromJson(anotherJson,*TheCorrespondingObject.class*)

I know I can achieve this by trying my JSON string with different objects on which Gson throws exception but I don't think that is the right way to do it.

Upvotes: 5

Views: 10525

Answers (3)

durron597
durron597

Reputation: 32333

The best, easiest way to do this is to wrap the JSON itself in a container that contains the type.

For example:

{
  "foo":"bar"
}

Becomes

{
  "type":"FooContainer"
  "value": {
     "foo":"bar"
  }
}

However, you said you can't do that, so I won't go into details about how to actually do the deserialization. If you can do this limited change, you can write a TypeAdapterFactory and let Gson do all the rest of the work, by using the passed Gson object in the create() method and calling gson.fromJson() in the read method, which will be a lot less code than the method below.


Think about what really needs to happen here. Somehow, you need to detect what data is in the Websphere MQ objects and use that to figure it out. Detecting it in this way is going to take code, to analyze what data you've received, and spit out the correct object.

The first problem you're going to face is that you don't want to do this:

Object myObj = gson.fromJson(jsonString, Object.class);

This means that you are overriding the default, base deserializer, which can have all sort of nasty side effects. It's better to wrap the behavior in the TypeAdapter for some sort of custom object:

public class Wrapper {
  public Object data; // Encapsulate this if you want, this is just for brevity
}

I'll leave the casting / generics to you. There are lots of ways to do it but I recommend the Visitor Pattern as a good choice, or perhaps creating an enum type object. It would be best if you can have all the received object types implement some sort of interface to make this easier, but, again, that's not your main problem.

Your main problem is actually detecting these classes in code. Now that we have a wrapper class, we can use this:

Wrapper myWrapper = gson.fromJson(jsonString, Wrapper.class);

Now, every class that's generated is the same class, so Gson is happy. But where do we put the rules? They go in your custom TypeAdapter:

public class WrapperTypeAdapter extends TypeAdapter<Wrapper> {
    @Override
    public final void write(JsonWriter out, Wrapper value) throws IOException {
        if(value == null) {
            out.nullValue();
            return;
        }

        // Don't worry about Write for now, obviously
    }

    @Override
    public final Wrapper read(JsonReader in) throws IOException {
        if(in.peek() == JsonToken.NULL) {
            in.nextNull();
            return null;
        }

        Wrapper wrapper = new Wrapper();

        // HERE IS WHERE YOU GET TO WRITE CODE

        return wrapper;
    }
}

In the read method, as you stream tokens, you can use rules based on what you expect the JSON tokens themselves to be to decide what class it's going to be, then construct the new object. I recommend breaking this down into many methods like MyFoo readMyFoo(JsonReader), MyBar readMyBar(JsonReader), etc. It's difficult for me to go into much more detail without knowing more about the JSON itself, but this should be enough to start you off.

Upvotes: 6

Rustam
Rustam

Reputation: 1407

Manually by analyzing original string:

if (jsonString.indexOf(..) > 0) {
   //reading Person
} else {
   //reading SomeRandomObject
}

or you can use JSON Simple and read any JSON

Upvotes: 0

Haroldo_OK
Haroldo_OK

Reputation: 7260

Generic answer: You'll have to add something to the generated JSON to indicate what it was generated from.

Say, instead of

{
    "name": "John"
}

You would generate something like:

{
    "type": "Customer"
    "name": "John"
}

How to do this in GSON is left as an exercise to the reader. ;)


Edit: Oh, you can't tamper with the JSON. Nevermind, then.

Still, it would be difficult to do this in a generic way without some kind of identifier, specially taking into consideration that some classes may happen to have exactly the same fields.

Upvotes: -1

Related Questions