Как сделать так, чтобы один индивидуальный сериализатор GSON применялся ко всем подклассам? - PullRequest
0 голосов
/ 12 октября 2018

Я использую GSON для применения универсального сериализатора ко всем подклассам абстрактного Base класса.Тем не менее, GSON не будет вызывать мой сериализатор, если ему даны фактические подклассы класса Base, если явно не указано использовать Base.class в качестве приведения.Вот простой пример того, о чем я говорю.

public interface Base<T>{
  String getName();
  public List<Object> getChildren();
}

public class Derived1 implements Base<Integer>{
  private Integer x = 5; 
  String getName(){ 
    return "Name: " + x;
  }
  List<Object> getChildren(){
    return Lists.newArrayList(new Derived2(), "Some string");
  }
}

public class Derived2 implements Base<Double>{
  private Double x = 6.3;
  String getName(){
    return "Name: " + x;
  }
  List<Object> getChildren(){
    return new List<>();
  }
}

Я создаю сериализатор следующим образом:

JsonSerializer customAdapter = new JsonSerializer<Base>(){
  @Override
  JsonElement serialize(Base base, Type sourceType, JsonSerializationContext context){
    JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("name", base.getName());
    JsonArray jsonArray = new JsonArray();
    for (Object child : base.getChildren()){
      jsonArray.add(context.serialize(child));
    }
    if (jsonArray.size() != 0){
      jsonObject.add("children", jsonArray);
    }
  }
};

Gson customSerializer = new GsonBuilder()
  .registerTypeAdapter(Base.class, customAdapter)
  .create();

Однако, применяя мой собственный сериализатор к Listиз подклассов не имеет желаемого эффекта.

customSerializer.toJson(Lists.newArrayList(new Derived1(), new Derived2()));

Это применяет сериализацию GSON по умолчанию к моим подклассам.Есть ли простой способ заставить мой пользовательский сериализатор использовать мой пользовательский адаптер на всех подклассах родительского класса?Я подозреваю, что одним из решений является использование отражения для итерации по всем подклассам Base и регистрации пользовательского адаптера, но я бы хотел, если возможно, избежать чего-то подобного.

Примечание: мне все равноо десериализации прямо сейчас.

1 Ответ

0 голосов
/ 12 октября 2018

Может быть, вам не следует использовать JsonSerializer.А именно, это возможно, если вы используете TypeAdapter, совершая ту же магию, регистрируя TypeAdapterFactory, который сообщает Gson, как сериализовать любой класс.

См. Ниже TypeAdapterFactory и TypeAdapter в нем:

public class CustomAdapterFactory implements TypeAdapterFactory {

    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        // If the class that type token represents is a subclass of Base
        // then return your special adapter 
        if(Base.class.isAssignableFrom(typeToken.getRawType())) {
            return (TypeAdapter<T>) customTypeAdapter;          
        }
        return null;
    }

    private TypeAdapter<Base<?>> customTypeAdapter = new TypeAdapter<Base<?>>() {
        @Override
        public void write(JsonWriter out, Base<?> value) throws IOException {
            out.beginObject();
            out.value(value.getName());
            out.endObject();            
        }

        @Override
        public Base<?> read(JsonReader in) throws IOException {
            // Deserializing to subclasses not interesting yet.
            // Actually it is impossible if the JSON does not contain 
            // information about the subclass to which to deserialize
            return null;
        }

    };
}

Если вы сделаете что-то вроде этого:

@Slf4j
public class SubClassTest {
    @Test
    public void testIt() {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapterFactory(new CustomAdapterFactory())
                .create();
        log.info("\n{}", gson.toJson(new Derived1()));
        log.info("\n{}", gson.toJson(new Derived2()));
    }
}

, результат будет таким:

2018-10-12 23: 13: 17.037INFO org.example.gson.subclass.SubClassTest: 19 - {"name": "Name: 5"} 2018-10-12 23: 13: 17.043 INFO org.example.gson.subclass.SubClassTest: 20 - {"name":" Имя: 6.3 "}

Если это не совсем то, что вам нужно, просто исправьте метод write(..) в customTypeAdapter.

...