Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registering multiple adapters with GSON isn't working

Tags:

java

json

gson

I'm having an issue registering multiple typeAdapters with my GsonBuilder. Seems like only one will fire off and it never takes the second one into considering. If i do it with each one by itself it seems to work fine. But I need them to work with both and seems that I am doing something wrong. Also I am currently using GSON v2.2.4.

Zip Object simple form:

public class Zip {

  private String zipCode;
  private String city;
  private String state;

  public Zip(){}                                            
}

ZipSerializer:

public class ZipSerializer implements JsonSerializer<Zip>{

    @Override
    public JsonElement serialize(Zip obj, Type type, JsonSerializationContext jsc) {
        Gson gson = new Gson();
        JsonObject jObj = (JsonObject)gson.toJsonTree(obj);
        jObj.remove("state");        
        return jObj;
    }

}

JsonResponse Object simple form:

public class JsonResponse {

    private String jsonrpc = "2.0"; 
    private Object result = null;
    private String id = null;

    public JsonResponse(){}

}

JsonResponseSerializer:

public class JsonResponseSerializer implements JsonSerializer<JsonResponse> {

    @Override
    public JsonElement serialize(JsonResponse obj, Type type, JsonSerializationContext jsc) {
        Gson gson = new Gson();
        JsonObject jObj = (JsonObject)gson.toJsonTree(obj);
        jObj.remove("id");

        return jObj;
    }

}

Test Example:

public class Test {

    public static void main(String[] args) {
        Zip zip = new Zip();
        zip.setCity("testcity");
        zip.setState("OH");
        zip.setZipCode("12345");

        JsonResponse resp = new JsonResponse();
        resp.setId("1");
        resp.setResult(zip);
        resp.setJsonrpc("2.0");

        Gson gson1 = new GsonBuilder()
        .registerTypeAdapter(JsonResponse.class, new JsonResponseSerializer())
        .registerTypeAdapter(Zip.class, new ZipSerializer())
        .setPrettyPrinting()
        .create();


        String json = gson1.toJson(resp);
        System.out.println(json);               

    }

}

Output:

{
  "jsonrpc": "2.0",
  "result": {
    "zipCode": "12345",
    "city": "testcity",
    "state": "OH"
  }
}

Expected Output: (Notice ZipSerializer didn't fire off)

{
  "jsonrpc": "2.0",
  "result": {
    "zipCode": "12345",
    "city": "testcity"
  }
}

I simpled down the test case for this. I know I can use exclusionStrategy in this example to achieve results but the real issue is much more complex and this was the best way for me to portray and replicate my issue.

Thanks

<---------------------------- SOLUTION ------------------------------->

I managed to read Gson custom seralizer for one variable (of many) in an object using TypeAdapter and it helped me out greatly.

I created a base customTypeAdapterFactory and then extended it for each class that needed special serialization.

CustomizedTypeAdapterFactory

public abstract class CustomizedTypeAdapterFactory<C> implements TypeAdapterFactory {

    private final Class<C> customizedClass;

    public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
        this.customizedClass = customizedClass;
    }

    @SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
    public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        return type.getRawType() == customizedClass
                ? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
                : null;
    }

    private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
        final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
        final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);

        return new TypeAdapter<C>() {

            @Override public void write(JsonWriter out, C value) throws IOException {               
                JsonElement tree = delegate.toJsonTree(value);
                beforeWrite(value, tree);
                elementAdapter.write(out, tree);
            }


            @Override public C read(JsonReader in) throws IOException {
                JsonElement tree = elementAdapter.read(in);
                afterRead(tree);
                return delegate.fromJsonTree(tree);
            }
        };
    }

    /**
    * Override this to muck with {@code toSerialize} before it is written to
    * the outgoing JSON stream.
    */
    protected void beforeWrite(C source, JsonElement toSerialize) {
    }

    /**
    * Override this to muck with {@code deserialized} before it parsed into
    * the application type.
    */
    protected void afterRead(JsonElement deserialized) {
    }
}

ZipTypeAdapterFactory (Did this to with JsonResponseTypeAdapterFactory)

public class ZipTypeAdapterFactory  extends CustomizedTypeAdapterFactory<Zip> {

    ZipTypeAdapterFactory() {
        super(Zip.class);
    }

    @Override
    protected void beforeWrite(Zip source, JsonElement toSerialize) {
        JsonObject obj = (JsonObject) toSerialize;
        obj.remove("state");
    }

}

Test Code:

Gson gson1 = new GsonBuilder()
        .registerTypeAdapterFactory(new JsonResponseTypeAdapterFactory())       
        .registerTypeAdapterFactory(new ZipTypeAdapterFactory())
        .setPrettyPrinting()
        .create();


        String json = gson1.toJson(resp);
        System.out.println(json);

Thank you everyone for the help.

like image 407
dayv2005 Avatar asked Aug 01 '13 02:08

dayv2005


1 Answers

Problem is in your JsonResponseSerializer. You create a new Gson instance, then on this new instance ZipSerializeris not registered. That's why your second serializer is never called.

If you want to achieve delegation and complex serialization, look at TypeAdapterFactory.

As you said, if you simply want to filter fields from serialization define a ExclusionStrategy.

like image 64
PomPom Avatar answered Oct 16 '22 15:10

PomPom