fyhack / google-gson

Automatically exported from code.google.com/p/google-gson
0 stars 0 forks source link

First class support for polymorphism (subclasses!) #231

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
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

GoogleCodeExporter commented 9 years ago
Issue 170 has been merged into this issue.

Original comment by limpbizkit on 28 Aug 2010 at 6:03

GoogleCodeExporter commented 9 years ago
It would be more robust to support a custom JSON schema that serializes runtime 
type information explicitly (akin to xsi:type in XML) rather than trying to 
guess the type by field name matching (if two unrelated classes had the same 
fields the guess might be wrong). For example, you could have 
Gson.setRuntimeTypeProperty(String) and say 
gson.setRuntimeTypeProperty("$type");

Thanks,
Adrian Price
Senior Architect
TIBCO Software Inc.

Original comment by adrianp....@gtempaccount.com on 1 Oct 2010 at 1:10

GoogleCodeExporter commented 9 years ago
Issue 237 has been merged into this issue.

Original comment by limpbizkit on 6 Oct 2010 at 5:49

GoogleCodeExporter commented 9 years ago
Issue 238 has been merged into this issue.

Original comment by limpbizkit on 6 Oct 2010 at 5:57

GoogleCodeExporter commented 9 years ago
Issue 209 has been merged into this issue.

Original comment by limpbizkit on 6 Oct 2010 at 6:35

GoogleCodeExporter commented 9 years ago
I concur with Adrian, except that it would be best if the JSON property holding 
the explicit runtime type were not configurable at all, just for simplicity's 
sake. However, you can only do that if the prop name is something that can't be 
a Java identifier name, to avoid clashing. I'm guessing the serializer would 
have to be configured for whether or not to output that information, maybe only 
on particular fields or classes. But the deserializer would not have to 
configured; it would simply use the runtime type if present, and behave the old 
way if not.

At any rate, I would also like to see this done. Not being able to handle 
polymorphism is pretty much a non-starter for a rich data model. Is there a 
workaround, even if painful? If so, could you post something showing how it 
could be done?

- Ray A. Conner

Original comment by ray.a.co...@gmail.com on 27 Oct 2010 at 9:25

GoogleCodeExporter commented 9 years ago
Issue 129 has been merged into this issue.

Original comment by limpbizkit on 4 Nov 2010 at 10:44

GoogleCodeExporter commented 9 years ago
Let me give another counter-example (pseudo-code), which I would really like to 
turn into json:

class AllPredicate< T > implements Predicate< T > {
  List< Predicate< T > > operands;
}

I always avoid specifying a concrete type if an appropriate interface exists.

Original comment by ray.a.co...@gmail.com on 11 Nov 2010 at 12:10

GoogleCodeExporter commented 9 years ago
I disagree with the suggestion in Comment#6 that the type property name should 
be hard-coded. We're talking about a *custom* JSON schema so IMO such a 
property needs to be configurable (just as the mechanism for resolving and 
deresolving the type name needs to be extensible, to allow use of other 
metamodels besides Java, e.g., XSD, UML, EMF). I suppose you could store the 
custom type property name in some hard-coded property on the root JSON object 
(e.g., gson:type_property) and of course there should be a sensible default 
type property name (e.g., gson:type) and a usable default metamodel (e.g., 
JavaTypeSystem).

By the way there are other custom JSON schema extensions that are also required 
to support *formal modelling* semantics properly, such as the notion of element 
identity and the distinction between UML-style composition aggregation 
associations versus non-aggregating associations. As things currently stand a 
Java object graph in which multiple elements refer to a single object, the 
referenced object will be replicated in the serialized JSON and will 
deserialize into multiple identical objects rather than a single instance as in 
the original. Not at all what is required of a non-aggregating association!

Adrian Price

Original comment by adrianp....@gtempaccount.com on 11 Nov 2010 at 2:12

GoogleCodeExporter commented 9 years ago
I'll admit it's a bad idea to hard-code that. My main concern was keeping the 
number of things that the programmer has to do to a minimum, convention over 
configuration. I've found that the more you make a programmer do, especially if 
it has to be consistent in two or more places, the more likely it will be done 
incorrectly.

Original comment by ray.a.co...@gmail.com on 11 Nov 2010 at 2:46

GoogleCodeExporter commented 9 years ago
You should try the Hierarchical Type Adapter feature to see if this helps 
simplify your problem. Basically, it appears that you will need to push the 
sub-classing logic into it.

The official announcement for the release is here:
http://groups.google.com/group/google-gson/browse_thread/thread/6272c9be58676e47

Original comment by joel.leitch@gmail.com on 13 Apr 2011 at 4:22

GoogleCodeExporter commented 9 years ago
Fixed in r828.

Original comment by joel.leitch@gmail.com on 20 Apr 2011 at 10:35

GoogleCodeExporter commented 9 years ago
Issue 321 has been merged into this issue.

Original comment by limpbizkit on 4 May 2011 at 9:31

GoogleCodeExporter commented 9 years ago
This wasn't fixed by r828. But I'm going to implement this. Here's my proposed 
API:

    RuntimeTypeAdapter<BillingInstrument> grta
         = new RuntimeTypeAdapter(BillingInstrument.class, "type");
    grta.registerSubtype(CreditCard.class, "CC");
    grta.registerSubtype(Paypal.class, "PayPal");
    grta.registerSubtype(BankTransfer.class); // defaults to the simple name, "BankTransfer"

This would synthesize a type field in the emitted JSON as a hint during 
deserialization:
    /*
     *    "billingInstruments": [
     *       {
     *         "type": "CC",
     *         "cvv": 234
     *       },
     *       {
     *         "type": "PayPal",
     *         "email", "jesse@swank.ca"
     *       }
     *    ]
     */

Original comment by limpbizkit on 5 May 2011 at 7:00

GoogleCodeExporter commented 9 years ago
Again, I urgently entreat you *not* to hard code the custom schema in the way 
suggested by comment #14. For a start, the namespace for 'meta-type' attributes 
like this *must* be something that won't collide with application metamodels. 
If you hard code it as 'type' then you won't be able to serialize any object 
that has a legitimate 'type' attribute. They made the same mistake in SDO and 
it ruined an otherwise lovely specification. At the very least, provide a 
mechanism for customizing such meta-attribute names.

Original comment by adrianp....@gtempaccount.com on 7 May 2011 at 12:51

GoogleCodeExporter commented 9 years ago
@adrianp agreed. Thats howhy 'new RuntimeTypeAdapter' takes two arguments!

Original comment by limpbizkit on 7 May 2011 at 3:54

GoogleCodeExporter commented 9 years ago
Ahh... I get it now. Sorry, I should have studied the code snippet more 
carefully. Looks good! :-)

Original comment by adrianp....@gtempaccount.com on 7 May 2011 at 5:42