How do I implement TypeAdapterFactory in Gson?

23,853

When you register a regular type adapter (GsonBuilder.registerTypeAdapter), it only generates a type adapter for THAT specific class. For example:

public abstract class Animal { abstract void speak(); }
public class Dog extends Animal {
   private final String speech = "woof";
   public void speak() {
       System.out.println(speech);
   }
}

// in some gson related method
gsonBuilder.registerTypeAdapter(Animal.class, myTypeAdapterObject);
Gson g = gsonBuilder.create();
Dog dog = new Dog();
System.out.println(g.toJson(dog));

If you did this, then Gson will not use your myTypeAdapterObject, it will use the default type adapter for Object.

So, how can you make a type adapter object that can convert ANY Animal subclass to Json? Create a TypeAdapterFactory! The factory can match using the generic type and the TypeToken class. You should return null if your TypeAdapterFactory doesn't know how to handle an object of that type.

The other thing TypeAdapterFactory can be used for is that you can't CHAIN adapters any other way. By default, Gson doesn't pass your Gson instance into the read or write methods of TypeAdapter. So if you have an object like:

public class MyOuterClass {
    private MyInnerClass inner;
}

There is no way to write your TypeAdapter<MyOuterClass> that knows how to use your TypeAdapter<MyInnerClass> without using the TypeAdapterFactory. The TypeAdapterFactory.create method DOES pass the Gson instance, which allows you to teach your TypeAdapter<MyOuterClass> how to serialize the MyInnerClass field.


Generally, here is a good standard way to begin to write an implementation of a TypeAdapterFactory:

public enum FooAdapterFactory implements TypeAdapterFactory {
    INSTANCE; // Josh Bloch's Enum singleton pattern

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

        // Note: You have access to the `gson` object here; you can access other deserializers using gson.getAdapter and pass them into your constructor
        return (TypeAdapter<T>) new FooAdapter();
    }

    private static class FooAdapter extends TypeAdapter<Foo> {
        @Override
        public void write(JsonWriter out, Foo value) {
            // your code
        }

        @Override
        public Foo read(JsonReader in) throws IOException {
            // your code
        }
    }
}
Share:
23,853

Related videos on Youtube

Suzan Cioc
Author by

Suzan Cioc

Not to be offended

Updated on August 30, 2020

Comments

  • Suzan Cioc
    Suzan Cioc over 3 years

    How do I implement type TypeAdapterFactory in Gson?

    The main method of create is generic. Why?

    The registration method registerTypeAdapterFactory() does not receive type a type argument. So, how does Gson know which classes are processed by the factory?

    Should I implement one factory for multiple classes, or can I implement one for many classes?

    If I implement one factory for multiple classes, then what should I return in case of out-of-domain type argument?

  • Suzan Cioc
    Suzan Cioc about 10 years
    So, will Gson call my adapter factory for hundreds of classes met during serialization?
  • durron597
    durron597 about 10 years
    yes, but Gson keeps a lookup table of class to AdapterFactory so it only tries your class once. if create returns null it never tries for that class again.
  • Suzan Cioc
    Suzan Cioc about 10 years
    where is it documented?
  • durron597
    durron597 about 10 years
    @SuzanCioc Umm yeah... I figured that out looking at the source code :-D
  • durron597
    durron597 about 10 years
    @SuzanCioc This doesn't explain that bit of it but it is still a very good reference: google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/‌​…
  • Subby
    Subby about 8 years
    If FooAdapter is the only Adapter that can be provided then why use an TypeAdapterFactory? Why not just use a TypeAdapter?
  • durron597
    durron597 about 8 years
    Because a TypeAdapter<T> can only serialize one specific type, T. If you want to serialize an interface and all implementors, you need to create a reference in the Gson lookup table for all requested classes that implement that interface.
  • Azamat Almukhametov
    Azamat Almukhametov about 7 years
    Could you kindly write deserialize example with Animal interface and Cat, Dog class ? Thanks!
  • Adam
    Adam about 5 years
    This work-around works, but clashes with my ExclusionStrategy which tells gson to ignore some fields. Gson obviously removes all reference to the fields excluded before it gets to the deserialization step, because those ignored fields in the JSON now causes the ValidationTypeAdapter to throw an unknown field exception!