Gson десериализует вложенный объект, который расширяет определенный класс, используя пользовательский метод

#java #json #gson #deserialization #pojo

#java #json #gson #десериализация #pojo

Вопрос:

У меня есть абстрактный класс, как указано ниже

 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());
    }
}
 

Цель этого абстрактного класса — обеспечить функциональность отслеживания переменных, которые были инициализированы в классах, которые расширяют это. Зачем мне это нужно? Я моделирую класс pojo для форм, которые будут использоваться в проекте автоматизации тестирования. Где некоторые параметры являются необязательными, и если они не были инициализированы, соответствующие элементы формы не заполняются. В настоящее время я использую этот метод для создания экземпляра классов, расширяющих IndexedPojo класс:

 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;
}
 

Но теперь проблема в том, что некоторые из вложенных дочерних элементов также являются классами, 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 = "";
}
 

Как я могу инициализировать вложенный SomeNestedConfig nestedConfig , когда я инициализировал RuleSetConfigForm ? Есть ли какой-либо автоматизированный способ сделать это?

Ответ №1:

Это можно решить с помощью пользовательского 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);
}
 

Комментарии:

1. Это работает идеально. Благодаря вам мой код стал намного более гибким