根据值反序列化为通用成员

Deserialize to generic member based on value

我有一个像这样的通用 class:

public class Pojo<T> {
    
    @JsonProperty("value")
    public T[] values;
};

T 可以容纳 StringLocalDateTimeInteger。 String 和 Integer 之间的区别似乎工作正常,很可能是因为这些类型在序列化 JSON 文件中的表示方式不同。但是,当我的 JSON 对象中有日期时间时(参见示例),它无论如何都会被解析为字符串。

value 字段实际使用的类型由输入决定。虽然我在以下最小示例中确实了解了这些知识,但对于我的程序中此代码的所有使用情况并非如此。我只能依赖字段 value.

值的模式
public class Main {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

    String json = "{\"value\": \"2022-02-22T12:00:00\"}";
    Pojo<?> deserialized = mapper.readValue(json, Pojo.class);
    assert deserialized.value[0] instanceof LocalDateTime;
}

我还没有成功修改 JsonTypeInfo。如果此字段的所有值都匹配模式 yyyy-MM-dd'T'hh:mm:ss?

,是否可以将值字段解析为 LocalDateTime 对象数组

这是您的问题 - Pojo<?> deserialized = mapper.readValue(json, Pojo.class)

ObjectMapper 不知道要将 T 解析为什么类型。要告诉它您需要使用 readValue 重载和 TypeReference, or the overload with JavaType 的类型。我发现 JavaType 更容易阅读,所以这里有一个例子:

public class Test {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

        String json = "{\"value\": \"2022-02-22T12:00:00\"}";
        JavaType javaType = TypeFactory.defaultInstance().constructParametricType(Pojo.class, LocalDateTime.class);
        Pojo<LocalDateTime> deserialized = mapper.readValue(json, javaType);
        System.out.println(deserialized.values[0].toLocalDate());
        System.out.println(deserialized.values[0].toLocalTime());
    }
}

构造参数时 JavaType 第一个参数是 class 本身,接下来是具体的泛型类型。

编辑: 考虑到新信息,我能想到的最好办法是自定义反序列化器,它在运行时解析类型。像这样:

public class UnknownTypeDeserializer<T> extends StdDeserializer<Pojo<T>> {

    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

    public UnknownTypeDeserializer() {
        super((Class<?>) null);
    }

    @Override
    public Pojo<T> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException, JacksonException {
        JsonNode node = parser.getCodec().readTree(parser);
        String value = node.get("value").asText();
        Pojo<T> pojo = new Pojo<>();
        T[] arr;
        try {
            arr = (T[]) new LocalDateTime[]{LocalDateTime.parse(value, this.formatter)};
        } catch (Exception exc) {
            try {
                arr = (T[]) new Integer[]{Integer.parseInt(value)};
            } catch (NumberFormatException numberFormatException) {
                arr = (T[]) new String[]{value};
            }
        }
        pojo.values = arr;
        return pojo;
    }
}

想法是尝试解析为 LocalDateTime,如果不可能,请尝试解析为 int,如果不可能,再次保留为 String。这是相当丑陋的做法,但我只是想说明这个想法。与其捕获异常,不如检查格式,例如使用正则表达式,并根据哪个正则表达式匹配,解析为正确的类型可能更好。我用string和int测试了一下,确实可以。

public class Test {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addDeserializer(Pojo.class, new UnknownTypeDeserializer<>());
        mapper.registerModule(simpleModule);

        String json1 = "{\"value\": \"2022-02-22T12:00:00\"}";
        Pojo<?> deserialized1 = mapper.readValue(json1, Pojo.class);
        System.out.println("is date - " + (deserialized1.values[0] instanceof LocalDateTime));

        String json2 = "{\"value\": \"bla bla bla\"}";
        Pojo<?> deserialized2 = mapper.readValue(json2, Pojo.class);
        System.out.println("is string - " + (deserialized2.values[0] instanceof String));

        String json3 = "{\"value\": 41}";
        Pojo<?> deserialized3 = mapper.readValue(json3, Pojo.class);
        System.out.println("is int - " + (deserialized3.values[0] instanceof Integer));
    }
}