Skip to content

First class support for polymorphism (subclasses!) #231

Open

Description

@GoogleCodeExporter
GSON always operates on the static type of an object. It has no mechanism to 
operate on the object's runtime type.

It would be handy if GsonBuilder permitted a way to specify the known 
subclasses of a type. Then at serialization/deserialization these fields could 
be included:

  public void testPolymorphism() {
    Rectangle r = new Rectangle();
    r.width = 5;
    r.height = 7;
    Circle c = new Circle();
    c.radius = 3;

    List<Shape> shapes = new ArrayList<Shape>();
    shapes.add(r);
    shapes.add(c);

    Gson gson = new GsonBuilder()
        .create();

    String json = gson.toJson(shapes, new TypeToken<List<Shape>>() {}.getType());
    assertEquals("[{\"width\":5,\"height\":7},{\"radius\":3}]", json);
  }

  static class Shape {}

  static class Rectangle extends Shape {
    int width;
    int height;
  }

  static class Circle extends Shape {
    int radius;
  }

It would be extra awesome if it could use the set of fields in a stream to 
infer the type to instantiate. In the example above, it could use that the 
'radius' field as evidence that the runtime type should be a Circle. Or perhaps 
this could be user-configured too, such as this:
  new GsonBuilder()
    .runtimeType(Shape.class, Circle.class, "radius")
    .runtimeType(Shape.class, Rectangle.class, "width", "height")
    .create();

Original issue reported on code.google.com by limpbizkit on 28 Aug 2010 at 6:00

Activity

  1. kiptoomm commented on Oct 13, 2015

    @kiptoomm

    Was this ever released? I'm using Gson 2.3.1 and don't see it..

  2. Cy4n1d3 commented on Nov 13, 2015

    @Cy4n1d3

    I don't think so.
    Guess I'll either have to write some generic converters or switch back to Jackson, as I rely on loads of Collections filled with abstract classes for a project...

  3. JonasVautherin commented on Apr 7, 2016

    @JonasVautherin

    GsonFire does something that could help (see "Type Selectors" here)

  4. Cy4n1d3 commented on Apr 7, 2016

    @Cy4n1d3

    Didn't know about that, thanks for the heads up!
    Might come in handy some day 👍

  5. subha-sam commented on Jul 26, 2016

    @subha-sam

    Hey guys ,
    I have a similar usecase as this
    Class A{
    // fields
    }

    Class B extends A{
    // fields
    }

    Class C extends A{
    // fields
    }

    Class D extends A{
    // fields
    }

    Class Test extends A{
    public List contents = new ArrayList(); // it may contain A, B, C etc.
    private boolean isHeader = false;
    private String myTestString = "test";
    }
    I am trying to serialize "contents " collection using gson libarary.

    can someone tell me whats the solution for this. I found using RuntimeTypeAdaptorFactory would solve it but, I using gson 2..5, in that version I dont see this class.

    Struck with this issues for a past three days. writing custom serializations code for my usecase runs for pages.kindly help. whats the work around that the current gson library is having.

  6. inder123 commented on Jul 27, 2016

    @inder123
    Collaborator

    Copy over RuntimeTypeAdapterFactory code. It is meant to be used as copied source-code.

  7. mgerhardy commented on Mar 27, 2017

    @mgerhardy

    Add pull request #1055 that shows a few issues regarding Collection. I would be glad about feedback.

    My problem is that I need to deserialize Collections at runtime - thus I can't use the new TypeToken<List>() {}.getType() syntax. I only have a Class instance. Do I have to register some special type of Collection adapter or JsonDeserializer? I tried to add a deserializer but this wasn't possible because I lack needed types for fromJson calls.

    	private class RunTimeTypeCollectionAdapter implements JsonDeserializer<Collection> {
    		@Override
    		@SuppressWarnings({ "rawtypes", "unchecked" })
    		public Collection deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
    				throws JsonParseException {
    			if (json == null) {
    				return null;
    			}
    			final TypeToken<?> typeToken = TypeToken.get(typeOfT);
    			try {
    				Class<?> collectionClass = typeToken.getRawType();
    				if (collectionClass == List.class) {
    					collectionClass = ArrayList.class;
    				} else if (collectionClass == Set.class) {
    					collectionClass = HashSet.class;
    				} else if (collectionClass == Map.class) {
    					collectionClass = HashMap.class;
    				}
    				final Collection collection = (Collection) Class.forName(collectionClass.getTypeName()).newInstance();
    				final Class<?> rawType = typeToken.getRawType();
    				if (json.isJsonArray()) {
    					final JsonArray array = json.getAsJsonArray();
    					for (int i = 0; i < array.size(); ++i) {
    						final JsonElement element = array.get(i);
    						final Object deserialize = context.deserialize(element, rawType);
    						collection.add(deserialize);
    					}
    				} else {
    					// final Object deserialize = context.deserialize(json,
    					// rawType);
    					// collection.add(deserialize);
    				}
    				return collection;
    			} catch (final Exception e) {
    				logger.error(e.getMessage(), e);
    			}
    			return null;
    		}
    	}
    

    As you can see in this example, I am getting to a point where the deserializer doesn't get a json array - but MyClass (from Collection - this is a little bit weird for me - because I get those elements one by one, not via array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions