rharter / auto-value-gson

AutoValue Extension to add Gson De/Serializer support
Apache License 2.0
607 stars 103 forks source link

support unrecognised fields #267

Closed VysotskiVadim closed 2 years ago

VysotskiVadim commented 2 years ago

Companion PR of https://github.com/mapbox/mapbox-java/pull/1394

The problem

I want to process a json file, for example:

{
    "a": "a",
    "b": "b"
}

having a model which isn't aware of all the properties from the json file, for example:

@AutoValue
public class Model {

  public abstract String a();

  public abstract Builder toBuilder();

  public static Builder builder() {
    return new AutoValue_Model.Builder();
  }

  public static TypeAdapter<Model> typeAdapter(Gson gson) {
    return new AutoValue_Model.GsonTypeAdapter(gson);
  }

  @AutoValue.Builder
  public class Builder {
     public abstract Builder a(String value);
     public abstract Model build();
  }

}

but when I serialize the model back to json after processing I want the unrecognized property b to be present in json.

For example, when I deserialize, update property a to a value "c", and serialize json back I want the following output:

{
    "a": "c",
    "b": "b"
}

See more https://github.com/mapbox/mapbox-java/issues/1344

Desired behaviour in code

String json = "{\"a\": \"a\", \"b\": \"b\"}"; // the same source JSON as in the example ☝️ 
// serializing json to Model
GsonBuilder gson = new GsonBuilder();
JsonObject jsonObject = gson.create().fromJson(json, JsonObject.class);
gson.registerTypeAdapterFactory(GeneratedAdaptersFactory.create());
Model model = gson.create().fromJson(jsonObject, Model.class);
// updating the model
Model updatedModel = model.toBuilder().a("c").build();
// deserializing back to json
String outputJson =  gson.create().toJson(updatedModel);

assertEquals( "{\"a\": \"c\", \"b\": \"b\"}", outputJson);

Idea

I can manually add an unrecognized property to the model and mark it with @UnrecognisedJsonProperties annotation:

@AutoValue
public class Model {

  public abstract String a();

  @UnrecognisedJsonProperties
  abstract Map<String, Object> unrecognized();

  public abstract Builder toBuilder();

  public static Builder builder() {
    return new AutoValue_Model.Builder();
  }

  public static TypeAdapter<Model> typeAdapter(Gson gson) {
    return new AutoValue_Model.GsonTypeAdapter(gson);
  }

  @AutoValue.Builder
  public class Builder {
     public abstract Builder a(String value);
     abstract Builder unrecognized(Map<String, Object> value);
     public abstract Model build();
  }

}

If the generated type adapters were aware of the unrecognized property and put all the unrecognized fields from a parsed json there during reading, and wrote unrecognized properties from back to json file during writing I would get desired behavior.

VysotskiVadim commented 2 years ago

Sorry, wrong repo, it's supposed to be for the forked one