Future: Custom serialization for anotated filed ?

121 views
Skip to first unread message

Alexandr Maximov

unread,
Apr 24, 2013, 12:36:23 PM4/24/13
to googl...@googlegroups.com
Hi,

first of all thanks for a nice lib.

I am using Gson and I am quite happy with it. But as I am using the lib sometimes I am going with one problem:
Sometimes you need to serialize or deserialize one filed in 2 classes in a different way. Or a object as a separate class, and the same object as a field in another class. I didn't find any straightforward methods to do that.
I found 2 solutions for this problem: first one is creating a custom serializer/deserializer for the whole second class, and the second is to create another class with inheritance from first class, and use child class in one place and parent in another.

Neither first nor second solution is good. It will be more comfortable to have an annotation for the fields which you want to serialize in their own way, or something similar.
Could it be added as a future to the one of the future releases? I think will be a nice thing...

May be there is another workaround for this problem? If there are another nice solutions to this problem, let me know, please.

Inderjeet Singh

unread,
Apr 24, 2013, 12:47:59 PM4/24/13
to googl...@googlegroups.com
Hi Alexandr,

Glad to know that you like Gson. Positive feedback is appreciated!

IMHO, for the problem you are mentioning, custom serializers/deserializers seem perfectly suited. It seems to me any annotation will cover a few cases but leave out too many other. Did you have any specific annotation in mind that you can share?

Best
Inder

----

Alexandr Maximov

unread,
Apr 24, 2013, 1:44:21 PM4/24/13
to googl...@googlegroups.com
Hi Inder,
thank you for your response. I sought that it could work in the similar way like ExclusionStrategie works, so you could create you own annotation, annotate class or field and create custom serializer/deserializer and "SerializationStrategy" with parameter of serializer (or it could be implemented in other way, with constructor), after that just call  GsonBuilder() method something like setSerializationStrategies( SerializationStrategy );
Thanks.
Alex

Dne středa, 24. dubna 2013 18:47:59 UTC+2 Inderjeet Singh napsal(a):

Inderjeet Singh

unread,
Apr 26, 2013, 12:03:43 PM4/26/13
to googl...@googlegroups.com
Hi Alexandr,

What you are proposing is something that can be done with a JsonSerializer/JsonDeserializer or a TypeAdapterFactory today. You can define any custom annotation you want, register a type adapter factory that looks for that annotation and does something custom.

What do you think is needed beyond that?

Alexandr Maximov

unread,
Apr 29, 2013, 2:45:43 PM4/29/13
to googl...@googlegroups.com
Hi Inder,

thank you for the answer. I think it exactly what I need. It will be great to add it to user manual. And I exactly don't know hot to look for annotation with a factory. Could you please post an example?

Thanks.
Alex

Dne pátek, 26. dubna 2013 18:03:43 UTC+2 Inderjeet Singh napsal(a):

Inderjeet Singh

unread,
Apr 30, 2013, 1:00:02 PM4/30/13
to googl...@googlegroups.com
Here is what you need to do:

TypeAdapterFactory/JsonSerializer/JsonDeserializer provide you access to the "Type" of the object.
Convert the Type to its rawType/class. With the Class object, you can access all the fields and look up their annotations.

Inder

----

Alexandr Maximov

unread,
May 3, 2013, 6:32:47 PM5/3/13
to googl...@googlegroups.com
Hi Inder,

thank you for your reply.
I tried to implement you variant but it doesn't work as I thought.
For example I have 2 Classes Person { String id, String name, String surname }  and Room { String id, List<Person> persons }.
So I want a Person class to serialize/deserialize normally, and Room class serialize like Room {"id":"id", persons:["id1", "id2", "id3"]} and Room deserialize with applying custov deserializer on List<Person> wich find persons from DB by their id, and I want to do that without writing Serializer/Deserializer for the Room class, I want to apply custom serialization/deserialization on List<Person> in Room class.
I think it's not possible to make with TypeAdapterFactory. Or it's posiible but I should register it on Room, but it almost the same as write custom serializer/deserializer. Could it be doon easily in another way?

Thank you.

Alex.

Dne úterý, 30. dubna 2013 19:00:02 UTC+2 Inderjeet Singh napsal(a):

Inderjeet Singh

unread,
May 13, 2013, 5:17:36 PM5/13/13
to googl...@googlegroups.com
It should be possible with a TyepAdapterFactory.
Note that TypeAdapterFactory is invoked on "All types" so you can intercept the type and do what you need to.

You can also register JsonSerializer/JsonDeserializer for Room and handle the field persons in a custom way.

Inder

Tim Whitbeck

unread,
May 21, 2013, 3:48:27 PM5/21/13
to googl...@googlegroups.com
I wanted to share my solution to this problem and get some feedback as to whether or not there's a better way.
My situation is this: I'm using Hibernate for ORM, and I want to prevent Gson from attempting to serialize any Hibernate proxied object. My first solution was the same as Alexandr was saying: I had to create a custom JsonSerializer for each class. It quickly became apparent that I didn't want to continue with that method, it was a maintenance nightmare.

So here's what I came up with (with a lot of help from this SO answer: http://stackoverflow.com/a/11272452/1515258. Thanks, Jesse!)
When I'm creating my GsonBuilder, I register a TypeAdapterFactory which will be responsible for creating TypeAdapters for all of my Hibernate domain objects.
builder.registerTypeAdapterFactory(new TypeAdapterFactory() {
      @Override
      public <T> TypeAdapter<T> create(final Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);

        // First check if this object has the @Entity annotation
        if (!type.getRawType().isAnnotationPresent(Entity.class)) {
          // If not, return null (meaning this Factory won't be the one that creates a type adapter for this type)
          return null;
        }

        // This is an @Entity class
        return new TypeAdapter<T>() {
          @Override
          public void write(JsonWriter out, T value) throws IOException {
            // If the object is null
            if (value == null) {
              // Simply write out null
              out.nullValue();

              return;
            }

            out.beginObject();

            // For each field in the object
            for (Field field : value.getClass().getDeclaredFields()) {
              // Set the field to accessible, so we can see what the value is
              field.setAccessible(true);

              // Attempt to grab the value of the field
              Object fieldValue;
              try {
                fieldValue = field.get(value);
              } catch (IllegalArgumentException e) {
                e.printStackTrace();
                continue;
              } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
              }

              // If the field value is null, skip it
              if (fieldValue == null) {
                continue;
              }

              // If this field is not an uninitialized hibernate proxy
              if (Hibernate.isInitialized(fieldValue)) {
                Type fieldType = field.getGenericType();
                out.name(field.getName());
                gson.toJson(fieldValue, fieldType, out);
              }
            }

            out.endObject();
          }

          @Override
          public T read(JsonReader in) throws IOException {
            // Use the built-in TypeAdapter for this type to deserialize
            return delegate.read(in);
          }
        };
      }
    });

Since I'm using annotations with Hibernate, this factory checks for the presence of the @Entity annotation and if it finds it, it creates an adapter for that class. Deserialization just uses the delegate adapter, but serialization uses reflection to enumerate over all of the fields on the object, check if they're an uninitialized hibernate proxy (Hibernate.isInitialized), and skips those fields during serialization. I know this is a very crude implementation of a type adapter, and I'm sure it's fragile. One issue I'm now running into is that this TypeAdapterFactory doesn't take exclusion strategies into account, so now I'm needing to reimplement that internal part of Gson, as well.

Does anyone have any helpful suggestions for improving this TypeAdapterFactory or a better approach for doing this (excluding a field from serialization based on its value or runtime type)? The exclusionstrategy route didn't suffice, since it only gave me access to the declared type of the fields not the runtime type, and at runtime Hibernate wraps the field inside a proxy object.

Inderjeet Singh

unread,
May 29, 2013, 2:00:33 PM5/29/13
to googl...@googlegroups.com
Good solution.

One performance issue with this solution is that your TypeAdapter is doing reflection on value.getClass() for each request. You can improve it by doing the reflection once, and then mapping it to a specific type adapter that writes out the fields. Probably a fair bit of work but if the performance matters, you can go that route.

HTH
Inder
----
Reply all
Reply to author
Forward
0 new messages