簡體   English   中英

Jackson 使用屬性名稱作為值將 JSON 反序列化為泛型類型以從注冊表中獲取類型

[英]Jackson deserialize JSON into generic type using property name as value to fetch type from registry

我一直在嘗試通過使用屬性值作為對要為T反序列化的類型的引用並從注冊表中獲取類型來將 JSON 反序列化為泛型類型。

我能夠實現概念證明,如果我使用 TypeFactory.constructParametricType 提供正確的TypeFactory.constructParametricType ,我可以反序列化泛型類型,但后來我嘗試實現JsonDeserializer但它最終再次調用自身,因為該反序列化器已注冊到Property.class所以我嘗試的另一件事是使用新的ObjectManager ,但我真的不想創建新的 ObjectMapper 並且想使用同一個。

所以也許有人能夠指導我正確的方向。 (我知道注釋,但我不能真正使用它們,因為類型注冊表需要特定的邏輯)

這是我當前有效的臨時代碼:

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class Scratch {

  interface Type {

    @JsonIgnore
    public default String getName() {
      return this.getClass().getSimpleName();
    }
  }

  static class Property<T extends Type> {

    private String typeName;

    private T type;

    private String name;

    public Property() {

    }

    public Property(T type, String name) {
      this.typeName = type.getName();
      this.type = type;
      this.name = name;
    }

    public String getTypeName() {
      return typeName;
    }

    public void setTypeName(String typeName) {
      this.typeName = typeName;
    }

    public T getType() {
      return type;
    }

    public void setType(T type) {
      this.type = type;
      this.typeName = type.getName();
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }
  }

  static class TypeRegistry {

    private final Map<String, Class<? extends Type>> types = new HashMap<>();

    public void add(Type type) {
      types.put(type.getName(), type.getClass());
    }

    public boolean has(String typeName) {
      return types.containsKey(typeName);
    }

    public Class<? extends Type> get(String typeName) {
      return this.types.get(typeName);
    }
  }

  static class TextType implements Type {
    private String color;

    public String getColor() {
      return color;
    }

    public void setColor(String color) {
      this.color = color;
    }
  }

  static class NumberType implements Type {
    private String format;

    public String getFormat() {
      return format;
    }

    public void setFormat(String format) {
      this.format = format;
    }
  }


  public static void main(String[] args) throws Exception {
    var typeRegistry = new TypeRegistry();
    typeRegistry.add(new TextType());
    typeRegistry.add(new NumberType());

    var parameters = new ArrayList<Property<?>>();
    parameters.add(new Property<>(new TextType(), "text_field"));
    parameters.add(new Property<>(new NumberType(), "number_field"));

    var module = new SimpleModule();
    module.addDeserializer(Property.class, new JsonDeserializer<Property<?>>() {

      private final ObjectMapper newObjectMapper = new ObjectMapper();

      @Override
      public Property<?> deserialize(JsonParser p, DeserializationContext ctxt)
          throws IOException, JsonProcessingException {
        JsonNode node = p.getCodec().readTree(p);
        if (node == null || node.isNull()) {
          return null;
        }

        var typeName = node.get("typeName").asText();
        var type = typeRegistry.get(typeName);


        return newObjectMapper.readValue(
            node.toString(),
            ctxt.getTypeFactory().constructParametricType(Property.class, type)
        );
      }
    });

    var objectMapper = new ObjectMapper();

    var desObjectMapper = new ObjectMapper();
    desObjectMapper.registerModule(module);

    var jsonSingle = objectMapper.writeValueAsString(parameters.get(0));
    var json = objectMapper.writeValueAsString(parameters);

    var parsedSingle = objectMapper.readValue(
        jsonSingle,
        TypeFactory.defaultInstance().constructParametricType(
            Property.class,
            typeRegistry.get("TextType")
        )
    );

    var parsedProperties = desObjectMapper.readValue(
        json,
        new TypeReference<List<Property<?>>>() {}
    );
  }
}

謝謝你的幫助!

編輯 1(添加了@michael-gantman 提出的解決方案):

var parsedProperties = objectMapper.readValue(
        json,
        new TypeReference<List<LinkedHashMap<String, Object>>>() {
        }
    );

    var castedProperties = new ArrayList<Property<?>>();
    for (var parsedProperty : parsedProperties) {
      var typeName = (String) parsedProperty.get("typeName");

      castedProperties.add(
          objectMapper.convertValue(
              parsedProperty,
              TypeFactory.defaultInstance().constructParametricType(
                  Property.class,
                  typeRegistry.get(typeName)
              )
          )
      );
    }

這只是另一種方法。 始終將您的 Json 反序列化為Map<String, Object> (用於 JSON 對象)或List<Object> (用於 Json 列表)。 而不是根據您的屬性將您的地圖轉換為您的自定義類。 為此,您可以讓所有相關類都有一個接受 Map 或靜態方法的構造函數
T getInstance(Map<String, Object>)創建一個實例。 實現 Map -> 特定類轉換比 JSON -> 特定類更簡單。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM