Closed GoogleCodeExporter closed 9 years ago
Sorry, the example code has a small mistake, as I renamed one of the interfaces
before submitting the patch. Where it says "JsonDeserializedBy", it should say
"JsonDeserialization". The patch and the rest of my post are accurate.
Original comment by mint...@everlaw.com
on 12 Jan 2012 at 2:03
That's extremely clever!
FYI, if you're willing to make a single call to
GsonBuilder.registerTypeAdapterFactory(), I don't think you need to make any
changes to Gson 2.1 to make this work.
Original comment by jessewil...@google.com
on 12 Jan 2012 at 6:03
[deleted comment]
Will that actually work, though? From what I could tell looking at the code, a
TypeAdaptor is registered against a specific type that is later retrieved with
a map lookup. Since my method relies on any object simply implementing an
interface, doesn't that require a change to Gson?
The alternative would be for each object to register itself with some
globally-used Gson object. I preferred the interface approach.
Original comment by mint...@everlaw.com
on 18 Jan 2012 at 7:06
It'll work, but you need to register a TypeAdapterFactory, not a TypeAdapter.
The factory lets you support any type.
Original comment by jessewil...@google.com
on 21 Jan 2012 at 2:06
Thank you for the continued guidance, Jesse. A TypeAdapterFactory is definitely
the right way to do this, but I've run into a few issues with accessibility in
implementing it. I've attached my TypeAdapterFactory and the interfaces
associated with it, and I would appreciate any suggestions you can give me on a
better approach. I am aware that using ReflectiveTypeAdapter in the way that I
have is something of a hack (I should be following an approach like
TreeTypeAdapter's delegate()), but I don't think it changes the idea much.
There are a few calls to my own custom class called "Reflection". I am not
going to include it in the attachment, but I'll specify the methods instead:
Class classOfType(Type t)
Return the upper bound on t. If (t instanceof Class), it's simply ((Class) t).
For a ParameterizedType, e.g., List<String> would be List.class. For something
like "? extends Comparable", it's Comparable.class.
Class[] getTypeParameters(Class implClass, Class genClass)
Ascends and descends the class hierarchy between implClass and genClass to
return the array indicating genClass's instantiated type parameters as
specifically as possible. For any parameter that doesn't resolve completely,
the behavior follows that of classOfType(). (In its usage here, it's used to
find the actual class DESERIALIZER when a class implements
JsonDeserialization<DESERIALIZER>.)
T newInstance(Class<T> c)
Just like c.newInstance() except that exceptions are rethrown as a
RuntimeException and the accessibility is set to true before invoking the
constructor (so that non-public constructors can be invoked).
Field getAccesibleField(Class c, String fieldName)
Calls c.getDeclaredField(fieldName), rethrowing exceptions as a
RuntimeException and setting the resulting Field's accessibility to true and
before returning it.
Original comment by mint...@everlaw.com
on 6 Feb 2012 at 3:19
Attachments:
Yeah, you probably shouldn't have to do that much work. See the
TypeAdapterFactory documentation for an example that includes delegation:
http://google-gson.googlecode.com/svn-history/r1110/trunk/gson/docs/javadocs/com
/google/gson/TypeAdapterFactory.html
For serialization, you should delegate to the concrete class of the type being
serialized. For deserialization, you should delegate to the adapter of the
concrete class that implements your interface. You can get both type adapters
using the Gson instance passed in to create().
Original comment by jessewil...@google.com
on 6 Feb 2012 at 4:46
I'm sorry, but I'm having trouble following your suggestion. My mechanism
allows a class to implement one or both of JsonSerialization and
JsonDeserialization<DESERIALIZER>. If Obj obj implements the former, calls to
gson.toJson(obj) execute a callback to
obj.serialize(gson.serializationContext). If Obj implements the latter, calls
to gson.fromJson(Obj.class) effectively execute
DESERIALIZER.newInstance().deserialize(JsonElement je, Obj.class,
gson.deserializationContext).
I don't see how I can handle that with a delegate. I understand that I can
avoid my usage of ReflectiveTypeAdapter, but that's a more minor issue. The
issue I'm running up against is that in implementing my own TypeAdapterFactory,
I can't technically access gson.de/serializationContext without hacking around
the Java security system. This leads me to believe I'm doing something wrong.
In the case where I wish to override the default de/serialization, I don't
believe that I can perform the delegation you suggest because I'm not actually
registering a type adapter for every type implementing JsonSerialization and
JsonDeserialization (my two custom interfaces). Is there some hook to perform
that registration without resorting to hacks that use the Reflections package
to find every subclass of the aforementioned interfaces and register a
TypeAdapter for each?
The point of my factory is to allow objects to simply implement an interface
instead of having to register themselves with a canonical Gson instance. That
means that I can't "delegate to the concrete class of the type being
serialized" for classes that implement JsonSerialization because I need to
ensure that the object's serialize(jsc) method is called instead. I also don't
think I can delegate for deserialization because the deserializer is not
registered with any Gson instance.
Am I missing something simple?
Original comment by mint...@everlaw.com
on 6 Feb 2012 at 9:15
Got it. The JsonSerializationContext/JsonDeserializationContext APIs aren't
present nor necessary for streaming type adapters implementing the TypeAdapter
interface. Instead that interface uses 'Gson' which provides a superset of the
functionality of JsonSerializationContext and JsonDeserializationContext.
If you want, change your interface to take a Gson instance instead. I posted
another big TypeAdapterFactory example on issue 43; you may want to read it
through.
Original comment by jessewil...@google.com
on 7 Feb 2012 at 5:02
Perfect! Thank you very much. I'll reply here soon with a cleaner version of my
InterfaceTypeAdapterFactory mechanism. Perhaps it will turn out to be something
worth including in trunk after a few iterations.
Original comment by mint...@everlaw.com
on 7 Feb 2012 at 5:24
[deleted comment]
[deleted comment]
Thanks a lot for working through this issue with me. Attached is an
implementation of the mechanism I discussed. It uses the Drink example you
provided in issue 43 . My goal was to make it as simple as possible for an
implementor to perform the conversion to and from Json.
It's also available on github: https://github.com/BMintern/gson-interface
In order for it to work, it must be registered with the Gson instance:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new InterfaceAdapterFactory())
.create();
Note that I changed the deserializer class to implement JsonDeserializes
instead of JsonDeserializer. I felt that the method signature defined there was
more consistent with JsonSerialization (and slightly easier to use).
Note also that this provides a dead-simple way for a class to recursively
serialize itself.
Original comment by mint...@everlaw.com
on 9 Feb 2012 at 12:02
Attachments:
Nice work!
FYI Gson team, mintern's library is a use case for a public getNextAdapter()
method.
Original comment by limpbizkit
on 9 Feb 2012 at 1:05
Original issue reported on code.google.com by
mint...@everlaw.com
on 12 Jan 2012 at 1:59Attachments: