4

I have an interface

public interace ABC {
}

Implementation of this is as follows:

public class XYZ implements ABC {
    private Map<String, String> mapValue;
    public void setMapValue( Map<String, String> mapValue) {
        this.mapValue = mapValue;
    }  

    public  Map<String, String> getMapValue() {
        return this.mapValue
    }
}

I want to deserialize a class using Gson which is implemented as

public class UVW {
    ABC abcObject;
}

when I try to deserialize it like gson.fromJson(jsonString, UVW.class); it returns me null. jsonString is UTF_8 String.

Is it because of interface used in UVW class? If yes, how do I deserialize such class?

durron597
  • 31,968
  • 17
  • 99
  • 158
AFH
  • 132
  • 2
  • 9
  • Given only a field of type `ABC`, how can Gson know how to deserialize the JSON? – Sotirios Delimanolis Aug 11 '15 at 20:59
  • How to make Gson know that use implementation XYZ of ABC? – AFH Aug 11 '15 at 21:02
  • I don't believe Gson has such built-in functionality (polymorphic deserialization). You'd need to write a custom deserializer that takes some kind of hint from the JSON for how to deserialize it. Look at Jackson. It has this feature built into it. – Sotirios Delimanolis Aug 11 '15 at 21:03
  • @SotiriosDelimanolis That question is woefully out of date. He suggests using `RuntimeTypeAdapterFactory`, which is in the code base now (he says it isn't) and then suggests to use `JsonDeserializer`, which is basically deprecated now. – durron597 Aug 11 '15 at 21:39
  • You can provide an updated answer if you wish. No need to spread it across questions. – Sotirios Delimanolis Aug 11 '15 at 21:40
  • @durron597 I don't mind reopening (you can too), I just don't want answers all over the place. There are numerous posts explaining how to solve this problem. – Sotirios Delimanolis Aug 11 '15 at 21:57
  • @SotiriosDelimanolis I have a gold badge too, if I thought you were definitely wrong I would have just reopened; you reminded me of the right thing to do and I decided to leave it closed. Maybe I'll write a more canonical answer (on the other question) tomorrow. – durron597 Aug 11 '15 at 21:59

1 Answers1

5

You need to tell Gson to use XYZ when it deserializes ABC. You can do this using a TypeAdapterFactory.

Briefly, thus:

public class ABCAdapterFactory implements TypeAdapterFactory {
  private final Class<? extends ABC> implementationClass;

  public ABCAdapterFactory(Class<? extends ABC> implementationClass) {
     this.implementationClass = implementationClass;
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    if (!ABC.class.equals(type.getRawType())) return null;

    return (TypeAdapter<T>) gson.getAdapter(implementationClass);
  }
}

Here is a complete working test harness that illustrates this example:

public class TypeAdapterFactoryExample {
  public static interface ABC {

  }

  public static class XYZ implements ABC {
    public String test = "hello";
  }

  public static class Foo {
    ABC something;
  }

  public static void main(String... args) {
    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapterFactory(new ABCAdapterFactory(XYZ.class));
    Gson g = builder.create();

    Foo foo = new Foo();
    foo.something = new XYZ();

    String json = g.toJson(foo);
    System.out.println(json);
    Foo f = g.fromJson(json, Foo.class);
    System.out.println(f.something.getClass());
  }
}

Output:

{"something":{"test":"hello"}}
class gson.TypeAdapterFactoryExample$XYZ
Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158