Gson 使用自定义方法反序列化扩展特定 class 的嵌套对象

Gson deserialize nested object that extend specific class using custom method

我有一个摘要 class 如下所示

public abstract class IndexedPojo {
    List<String> keySet;

    public List<String> getKeySet() {
        return keySet;
    }

    public void setKeySet(List<String> keySet) {
        this.keySet = keySet;
    }
    public void setKeySet(JsonObject parameters) {
        this.keySet = new ArrayList<>(parameters.keySet());
    }
    public void setKeySet(Map<String,String> parameters) {
        this.keySet = new ArrayList<>(parameters.keySet());
    }
}

此摘要 class 的目的是提供跟踪在扩展此摘要的 classes 中初始化的变量的功能。为什么我需要这个?我正在为要在测试自动化项目中使用的表单建模 pojo class。其中一些参数是可选的,如果它们尚未初始化,则不会填充相应的表单元素。目前我正在使用此方法创建扩展 IndexedPojo class:

的 classes 的实例
public static <T extends IndexedPojo> T deserializeJsonTo(JsonObject parameters, Class<T> tClass) {
    Gson gson = new Gson();
    T instance = gson.fromJson(parameters, tClass);
    instance.setKeySet(parameters);
    return instance;
}

但现在的问题是一些嵌套子项也是 class 是 extend IndexedPojo,我想以相同的方式初始化它们。例如

public class RuleSetConfigForm extends IndexedPojo {
    @SerializedName("Key")
    SomeNestedConfig nestedConfig;    
}
public class SomeNestedConfig extends IndexedPojo {
    @SerializedName("Some Key")
    private String someOptionalParamater1 = "";

    @SerializedName("Some Key2")
    private String someOptionalParamater2 = "";

    @SerializedName("Some Key3")
    private String someOptionalParamater3 = "";
}

如何在初始化 RuleSetConfigForm 时初始化嵌套 SomeNestedConfig nestedConfig?有没有自动执行此操作的方法?

这可以使用自定义 TypeAdapterFactory 来解决,该自定义 TypeAdapterFactory 委托给默认适配器,然后调用您的 IndexedPojo.setKeySet(...) 方法:

class IndexedPojoTypeAdapterFactory implements TypeAdapterFactory {
  public static final IndexedPojoTypeAdapterFactory INSTANCE = new IndexedPojoTypeAdapterFactory();

  private IndexedPojoTypeAdapterFactory() { }

  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    // Only handle IndexedPojo and subclasses
    if (!IndexedPojo.class.isAssignableFrom(type.getRawType())) {
      return null;
    }

    // Get the default adapter as delegate
    // Cast is safe due to `type` check at method start
    @SuppressWarnings("unchecked")
    TypeAdapter<IndexedPojo> delegate = (TypeAdapter<IndexedPojo>) gson.getDelegateAdapter(this, type);
    // Cast is safe because `T` is IndexedPojo or subclass (due to `type` check at method start)
    @SuppressWarnings("unchecked")
    TypeAdapter<T> adapter = (TypeAdapter<T>) new TypeAdapter<IndexedPojo>() {
      @Override
      public void write(JsonWriter out, IndexedPojo value) throws IOException {
        delegate.write(out, value);
      }

      @Override
      public IndexedPojo read(JsonReader in) throws IOException {
        // Read JsonObject from JsonReader to be able to pass it to `IndexedPojo.setKeySet(...)`
        // afterwards
        // Note: JsonParser automatically parses in lenient mode, which cannot be disabled
        // Note: Might have to add handling for JSON null values
        JsonObject jsonObject = JsonParser.parseReader(in).getAsJsonObject();
        
        IndexedPojo value = delegate.fromJsonTree(jsonObject);
        value.setKeySet(jsonObject);
        return value;
      }
    };

    return adapter;
  }
}

然后在您的 deserializeJsonTo(...) 方法中使用这个工厂:

public static <T extends IndexedPojo> T deserializeJsonTo(JsonObject parameters, Class<T> tClass) {
  // Note: Could also store Gson instance in `static final` field
  Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(IndexedPojoTypeAdapterFactory.INSTANCE)
    .create();
  return gson.fromJson(parameters, tClass);
}