Skip to content
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

JsonSerializable interface to mark classes as serializable #2788

Open
AnAwesomGuy opened this issue Jan 13, 2025 · 1 comment
Open

JsonSerializable interface to mark classes as serializable #2788

AnAwesomGuy opened this issue Jan 13, 2025 · 1 comment

Comments

@AnAwesomGuy
Copy link

Problem solved by the feature

Allow classes to be marked as serializable without needing another class as the serializer.
Is this similar to #594?

Feature description

It would work similarly to Java's Comparable interface, so you wouldn't need a serializer in another class.
Currently, you need to either add a TypeAdapter as an external class through @JsonAdapter or GsonBuilder, but it would be much more convenient if the instance (of the object we're serializing) could serialize itself.

The implementation could look a little something like this (not the best code, triple nested anonymous classes my beloved) (I code in 4 spaces so the formatting may be a bit wonk):

public interface JsonSerializable {
  TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
      return !JsonSerializable.class.isAssignableFrom(type.getRawType()) ?
        null : // return early if it isn't JsonSerializable
        new TypeAdapter<>() { // SerializationDelegatingTypeAdapter? not sure what it does
          private TypeAdapter<T> delegate;
          private JsonSerializationContext context;

          @Override
          public void write(JsonWriter out, T value) throws IOException {
            if (value instanceof JsonSerializable) // implicit null check
              Streams.write(((JsonSerializable)value).serialize(type.getType(), context()), out);
            else
              delegate().write(out, value);
          }

          @Override
          public T read(JsonReader in) throws IOException {
            return delegate().read(in);
          }

          private TypeAdapter<T> delegate() { // taken from Excluder
            TypeAdapter<T> d = delegate;
            return d != null
              ? d
              : (delegate = gson.getDelegateAdapter(JsonSerializable.FACTORY, type));
          }

          private JsonSerializationContext context() {
            JsonSerializationContext jsc = this.context;
            return jsc != null
              ? jsc
              : (context = new JsonSerializationContext() { // expose GsonContextImpl?
                  @Override
                  public JsonElement serialize(Object src) {
                    return gson.toJsonTree(src);
                  }

                  @Override
                  public JsonElement serialize(Object src, Type typeOfSrc) {
                    return gson.toJsonTree(src, typeOfSrc);
                  }
              });
          }
        };
    }
  };

  JsonElement serialize(Type thisType, JsonSerializationContext context);
}

Alternatives / workarounds

Implement the serializer or TypeAdapter in another class.

@Marcono1234
Copy link
Collaborator

Marcono1234 commented Jan 13, 2025

Is this similar to #594?

To me it sounds as if this is the same feature request. The only difference might be that the other feature request also requests this for deserialization.

you need to either add a TypeAdapter as an external class through @JsonAdapter

Note that the adapter class can be a nested static class (so that it can access private fields of the enclosing class), but when referencing it in @JsonAdapter you might have to qualify its name with the name of the enclosing class. For example something like:

@JsonAdapter(MyClass.MyAdapter.class)
class MyClass {
  static class MyAdapter extends TypeAdapter<MyClass> {
    ...
  }

  ...
}

The following are just some personal thoughts on this:

  • Such a serialize method should probably use a JsonWriter parameter though instead of JsonElement as return type for better performance. And maybe it would also need a Gson parameter to allow looking up other adapters, e.g. to serialize field values.
  • If you also want to support deserialization, then you might also want to put the implementation in the same class so that both serialization and deserialization are close together.
    • However then the deserialization would probably happen through a static method invoked using reflection. So there is no compile-time verification that the method name and signature is correct.
    • Or if a non-static method is used, Gson would first have to create an instance with default values and then call the deserialize method on it. But then it cannot set any final fields.
  • Unlike TypeAdapterFactory whose created type adapters are cached by Gson, it seems here there is no way to cache anything for serialization. So the serialize method would have to look up delegate adapters every time it is called (assuming the serialize method has a Gson parameter, as mentioned above).
    This might not cause a big overhead though.

So personally I am not completely sure if that would really provide such a big advantage over using @JsonAdapter on a class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants