My favorites | Sign in
Project Home Downloads Issues Source
New issue   Search
for
  Advanced search   Search tips   Subscriptions
Issue 411: gson does not deserialize interfaces, even with an InstanceCreator
9 people starred this issue and may be notified of changes. Back to list
Status:  Accepted
Owner:  ----


Sign in to add a comment
 
Reported by jason.po...@gmail.com, Feb 20, 2012
What steps will reproduce the problem?

1. Create an interface that defines getters only
2. Create a concrete class that implements the interface, with local fields and accessors corresponding to the interface
3. Specify an InstanceCreator to create concrete instances of the interface
4. Serialize a concrete instance of the class using gson
5. Deserialize from the JSON string produced in step 4.

What is the expected output? What do you see instead?

Given that an InstanceCreator is specified, I would expect a concrete implementation of the interface corresponding to the type created in the InstanceCreator, with the fields set from the JSON string.

Instead, a concrete instance of the class is returned but the fields are NOT populated.

Using version 2.1

I get that this can be done by creating a custom serializer/deserializer for the type, but I don't get why that should be required.  If so I can just use a regular JSONObject and do it all myself.  Seems like it should be a fairly simple task.  Take the runtime type created by the InstanceCreator, match the fields in the instance against the values in the JSON string using reflection, set and repeat.

Am I missing something here?

Apr 11, 2012
Project Member #1 limpbizkit
The core problem is that we inspect the fields of the declared type, not the InstanceCreator-created type.
Status: Accepted
Apr 18, 2012
#2 roc...@gmail.com
if the interface defines setters has the same problem anyway.
Apr 30, 2012
#3 future...@gmail.com
This fails in 2.1 and 2.0.
Works fine in 1.7.2 though.
Jul 18, 2012
#4 wd.dewi...@gmail.com
Same problem.

"The core problem is that we inspect the fields of the declared type, not the InstanceCreator-created type"

Can this be solved and more important, will it be solved?

Aug 24, 2012
#5 ecoffet....@gmail.com
The same problem exists with abstract classes (and not only interfaces)
Sep 2, 2012
Project Member #6 limpbizkit
You can work around this problem by defining your own TypeAdapter for the interface type.
Oct 18, 2012
#7 bhavesh4...@gmail.com
multiple TypeAdapter cause infinite loop....why it is so??
Feb 4, 2014
#8 northrup...@gmail.com
I have something that you may want to pick up and run with.  

im using gwt autobeans and had to figure out a way to make gson work around incompatible proxies on the server side. what works is to use groovy to be the enclosing classloader during TypeAdapter registration and for groovy to copmile the adapter class and register it.  the generation code can be cleaned up and some references to my gson singletions exist here.  i think the generated fromJson method is now superfluous as well. this is the only groovy in my project, fwiw, using pure java syntax, so cleaner solutions exist with bytecode engineering, but this one was an easy sample to plug in.

cheers. 


import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
import groovy.lang.GroovyClassLoader;
import org.apache.commons.beanutils.PropertyUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;

public class GroovyGsonShimFactory {
  private  Map<Class, Method> shimMethods = new LinkedHashMap<>();

  private void generateGroovyProxy(Class ifaceClass) {
    String shimClassName = ifaceClass.getSimpleName() + "$Proxy";
    String ifaceClassCanonicalName = ifaceClass.getCanonicalName();
    String s = "import com.google.gson.*;\n" +
        "import org.apache.commons.beanutils.BeanUtils;\n" +
         "import java.lang.reflect.*;\n" +
        "import java.util.*;\n\n" +
        "public class "+shimClassName+" implements "+ifaceClassCanonicalName+" {\n" ;

    {
      PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(ifaceClass);
      for (PropertyDescriptor p : propertyDescriptors) {
        String name = p.getName();
        String tname = p.getPropertyType().getCanonicalName();
        s += "public " + tname + " " + name + ";\n";
        s += " " + p.getReadMethod().toGenericString().replace("abstract", "").replace(ifaceClassCanonicalName + ".", "") + "{return " + name + ";};\n";
        Method writeMethod = p.getWriteMethod();
        if (writeMethod != null)
          s += " " + writeMethod.toGenericString().replace("abstract", "").replace(ifaceClassCanonicalName + ".", "").replace(")", " v){" + name + "=v;};") + "\n\n";
      }
    }
    s+=        "  public static "+ifaceClassCanonicalName+" fromJson(String s) {\n" +
        "    return (" +ifaceClassCanonicalName+
        ")cydesign.strombolian.server.ddl.DefaultDriver.gson().fromJson(s, "+shimClassName+".class);\n" +
        "  }\n" +  
        "  static public interface foo extends InstanceCreator<"+ifaceClassCanonicalName+">, JsonSerializer<"+ifaceClassCanonicalName+">, JsonDeserializer<"+ifaceClassCanonicalName+"> {}\n" +
        "  static {\n" +
        "    cydesign.strombolian.server.ddl.DefaultDriver.builder().registerTypeAdapter("+ifaceClassCanonicalName+".class, new foo() {\n" +
        "      public "+ifaceClassCanonicalName+" deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {\n" +
        "        return context.deserialize(json, "+shimClassName+".class);\n" +
        "      }\n" +
        "\n" +
        "      public "+ifaceClassCanonicalName+" createInstance(java.lang.reflect.Type type) {\n" +
        "        try {\n" +
        "          return new "+shimClassName+"();\n" +
        "        } catch (Exception e) {\n" +
        "          e.printStackTrace(); \n" +
        "        }\n" +
        "        return null;\n" +
        "      }\n" +
        "\n" +
        "      @Override\n" +
        "      public JsonElement serialize("+ifaceClassCanonicalName+" src, Type typeOfSrc, JsonSerializationContext context) {\n" +
        "        LinkedHashMap linkedHashMap = new LinkedHashMap();\n" +
        "        try {\n" +
        "          BeanUtils.populate(src, linkedHashMap);\n" +
        "          return context.serialize(linkedHashMap);\n" +
        "        } catch (Exception e) {\n" +
        "          e.printStackTrace(); \n" +
        "        }\n" +
        "\n" +
        "        return null;\n" +
        "      }\n" +
        "    });\n" +
        "  }\n\n" +
        "};";

    System.err.println("" + s);
    ClassLoader parent = DefaultDriver.class.getClassLoader();
    GroovyClassLoader loader = new GroovyClassLoader(parent);

    final Class gClass = loader.parseClass(s);
    try {
      Method shimMethod = gClass.getMethod("fromJson", String.class);
      shimMethods.put(ifaceClass, shimMethod);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    }

  }

  public <T> T getShim(String json, Class<T> ifaceClass) {
    if (!shimMethods.containsKey(ifaceClass))
      generateGroovyProxy(ifaceClass);
    T shim = null;//= gson().shimMethods(json, CowSchema.class);
    try {
      shim = (T) shimMethods.get(ifaceClass).invoke(null, json);
    } catch (IllegalAccessException | InvocationTargetException e) {
      e.printStackTrace(); 
    }
    return shim;
  }
}

Sign in to add a comment

Powered by Google Project Hosting