0

I am considering switching over to Gson.

From my beginner's knowledge, I know of two ways to implement custom serializers and deserializers:

  1. JsonSerializer and JsonDeserializer, and
  2. TypeAdaptor.

Consider the following:

public class Pojo {
  @JsonAdaptor(MyCustomAdaptor.class)
  private Integer number;
}

class MyCustomAdaptor extends TypeAdaptor<Integer> {
  @Override
  public Integer read(JsonReader in) throws IOException {
    // ...
  }

  public void write(JsonWriter writer, Integer value) throws IOException {
    // ...
  }
}

I noticed that TypeAdaptor does not expose the Field of number. Nor is this the case with JsonSerializer and JsonDeserializer. I understand that by the time these classes are used in the marshaling/unmarshalling lifecycle, there is no need to know this information.

For a moment, let's pretend that the Field was exposed in TypeAdaptors. The following is a basic example of what I would do:

public class Pojo {
  @JsonAdaptor(MyCustomAdaptor.class)
  @FloatSerialize
  private Number number;
}

class MyCustomAdaptor extends TypeAdaptor<Number> {
  @Override
  public Number read(JsonReader in, Field field) throws IOException {
    // Do something if @FloatSerialize is present.
  }

  public void write(JsonWriter writer, Number value, Field field) throws IOException {
    // Do something if @FloatSerialize is present.
  }
}

I know this would not make sense because @JsonAdaptor can be used on classes and fields.

Also, I know there are better ways to accomplish the above example; it is just an example and is meaningless. Basically, I want to use annotations, on a per-field basis, to tell custom serializers/deserializers how to process.

Is this possible with Gson? Can you create a custom serializer/deserializer that exposes the annotated class/field/etc?

Oliver
  • 1,465
  • 4
  • 17
  • Not possible in Gson. Have a look at https://stackoverflow.com/questions/66260322/same-type-serialization-based-on-field-name-in-gson-without-annotations for an alternative solution that probably might be developed to accomplish your needs. – terrorrussia-keeps-killing Feb 20 '21 at 08:10
  • Another solution to get rid of annotations like `@FloatSerialize` is implementing a base (probably abstract) type adapter that can be enhanced for concrete types, and then applied to concrete fields using `@JsonAdapter` (say, `abstract class CustomAdapter`, `class FloatCustomAdapter extends CustomAdapter`, `@JsonAdapter(FloatCustomAdapter.class)`). – terrorrussia-keeps-killing Feb 20 '21 at 08:42
  • @fluffy Thank you for your response, but that is not the point of my post. The example I gave was a meaningless one. I am asking if you can create a custom serializer/deserializer that exposes the class/field? – Oliver Feb 20 '21 at 17:43
  • I don't see _how_ it really differs from what you're asking. As I told: you can't as it's not possible in Gson, but _somehow_ possible via type adapter factories when creating type adapters per a single type, not even a class, (even parameterized). Exposing fields is much trickier since Gson `ReflectiveTypeAdapterFactory` cannot be customized or enhanced like that. If it is not exactly the same what you're looking for, then does it mean workarounds aren't accepted? Well, okay. – terrorrussia-keeps-killing Feb 21 '21 at 08:25
  • @fluffy Thank you for that explanation. – Oliver Feb 21 '21 at 15:26

2 Answers2

0
@JsonAdaptor(MyCustomAdaptor.class)
private Integer number;

You've annotated an Integer field.

I noticed that TypeAdaptor does not expose the Field of number

I think there is something you misunderstand.

You provide the TypeAdaptor, you gives it the same type as your field:

class MyCustomAdaptor extends TypeAdaptor<Integer>

Then at serialization, you have the java objet you've annotated as an input to serialize to a string, then you have your field directly available as 'value' in a parameter:

public void write(JsonWriter writer, Integer value) throws IOException

At deserialization, you have a json string as input to deserialize to the object you've annotated. In the type adaptor this object is your field, that's why read method return an Integer.

Integer read(JsonReader in) throws IOException

It's up to you to return an Integer from the JsonReader.

You can use a Number instead of an Integer for your field, then you can have float,integer,short and more in your field.

For this you bring a NumberAdaptor:

class MyCustomAdaptor extends TypeAdaptor<Number> {
   
    Number read(JsonReader in, Field field) throws IOException;
    
    void write(JsonWriter writer, Number value) throws IOException; 
}

If you really want to think about 'field', then provide an adaptor for the whole objet:

class Pojo Adaptor extends TypeAdaptor< Pojo > {

  Pojo read(JsonReader in) throws IOException;

  void write(JsonWriter writer, Pojo value) throws IOException;
}

Then you have access to the whole object and its fields in the adaptor.

Same things form JsonSerializer/JsonDeserialize.

Tokazio
  • 516
  • 2
  • 18
  • Thank you for your response. As I mentioned in my question, the example I gave does not make sense. I am asking if you can create a custom serializer/deserializer that exposes the class/field? I've updated my question. – Oliver Feb 20 '21 at 17:40
  • You can write a serializer for the whole object, then you gain access to its fields. With reflection youvcan access annotations on these fields and do what you want – Tokazio Feb 20 '21 at 18:32
0

With your additional info:

class Pojo Adaptor extends TypeAdaptor< Pojo > {

  Pojo read(JsonReader in) throws IOException{
for(Field f: Pojo.class.getDeclaredFields()){
            f.steAccessible();
            for(Annotation a : f.getAnnotations()){
                // Do what you want here withe the field and its annotation
            }
    }

  void write(JsonWriter writer, Pojo value) throws IOException{

        for(Field f: Pojo.class.getDeclaredFields()){
            f.steAccessible();
            for(Annotation a : f.getAnnotations()){
                // Do what you want here with the field and its annotation
            }
        }
    }
}

I've wrote this from my memory but you've got the idea

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Tokazio
  • 516
  • 2
  • 18