-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Adapter Layering Feature (Java 6 Compatible) #1473
base: main
Are you sure you want to change the base?
Conversation
…mechanic to go back to the beginning of the stream
* @param objectAdapter This object must implement the {@link TypeAdapter} class | ||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern | ||
*/ | ||
public GsonBuilder registerTypeAdapterWithFillIn(final Type baseType, Object objectAdapter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this method, why not create a TypeAdapter implementation that users can extend to get fill-in features.
e.g.,
ReflectiveTypeAdapter that internally will use ReflectiveTypeAdapterFactory
import com.google.gson.internal.ConstructorConstructor; | ||
import com.google.gson.internal.Excluder; | ||
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; | ||
import com.google.gson.internal.bind.TreeTypeAdapter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid frivolous changes like adding final to method parameters, imports reordering, or formatting changes.
* class, it can expose a method that accepts a {@code JsonReader} as a parameter, allowing fill-in | ||
* functionality. | ||
*/ | ||
public class InstanceCreatorWrapper<T> implements InstanceCreator<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this class part of the public API?
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public abstract class ReflectiveTypeAdapter<T> extends TypeAdapter<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public API?
Overall, I am still not convinced why this needs to be part of Gson, and not be part of extras package. Overall, we prefer really small changes to the public APIs. |
Why this is useful: Suppose you have a class with a lot of fields. Most of them can be easily interpreted by the Gson reader, possibly with a field naming strategy. But some have such weird names that the field naming strategy might not necessarily properly match them to the target properties. Especially if the JSON comes from a third party (like an API), the best course of action as of today is to create your own
TypeAdapter
and manually map all of the fields, including the ones that GSON could map on it's own. This is why I added the "Fill-In" feature. TheregisterTypeAdapterWithFillIn
method allows you to manually define only the fields that Gson can not map properly, while not having to map trivial mappings.Implementation Details: The way to register a custom adapter with Fill-in is through the
GsonBuilder#registerTypeAdapterWithFillIn
method. This method wraps the adapter into a constructor object that is then passed to an instance of theReflectiveTypeAdapterFactory.Adapter
class. In other words, when theread
method is called on aReflectiveTypeAdapterFactory.Adapter
instance, theObjectContstructor
object calls the custom adapter and generates an instance of the target class whose fields are written to or overwritten (see the Conditions section).When calling the
create
method on theReflectiveTypeAdapterFactory
responsible for producing a fill-in adapter, it will only return the adapter when the target type is or is a subclass of the desired target class. This is unlike the usualReflectiveTypeAdapterFactory
instances which serve to create a catch-all type adapter for non-standard classes.In order to support the construction of instances while reading from the input
JsonReader
, theInstanceCreator
andObjectConstructor
have methods that accept aJsonReader
object. If not overridden by the implementation, they default to the normalconstruct
method (for backwards compatibility).I wrote some tests in the
test/.../gson/internal/bind/LayeredAdapterTest.java
class to demonstrate and test some common use cases that I thought of for this feature.Notes:
@Expose
annotation to the fields that you want to be protected from overwriting.Due to the use ofI made this compatible with Java 6. See Below.default
methods in interfaces, this only supports java versions of 8+, and will fail the travis-ci check. I would really appreciate if someone pointed out a way to achieve the same thing in a backwards-compatible way without relying on Java 8 features. Also, if there is going to be a multi-release jar as suggested in java.lang.NumberFormatException: For input string: "11-ea" #1469, this can go in the java 8+ jar.Java 6 Compatibility: I added two wrapper classes,
InstanceCreatorWrapper
andObjectConstructorWrapper
, that wrap theInstanceCreator
andObjectConstructor
interfaces respectively. Therefore, it's backwards compatible since these creators and just implementations of their respective interfaces. However, when the fill-in part of the code tries to use them to create an instance from the contents of theJsonReader
object, it will case the objects returned by theConstructorConstructor
to the proper Wrapper, and use the special methods of those classes to create the object.