jackson 根据值反序列化为泛型成员

dly7yett  于 2022-11-09  发布在  其他
关注(0)|答案(1)|浏览(126)

我有一个泛型类,如下所示:

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对象的数组?

kuuvgm7e

kuuvgm7e1#

这就是你的问题-Pojo<?> deserialized = mapper.readValue(json, Pojo.class)
ObjectMapper不知道要将T解析为什么类型。要告诉它类型,您需要使用readValue重载和TypeReference,或者使用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时,第一个参数是类本身,接下来是具体的泛型类型。

**编辑:**记住新信息,我能想到的最好的是自定义反序列化器,它在运行时解析类型。类似于:

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。这是一个相当丑陋的方式来做,但我只是试图说明这个想法。与其捕捉异常,不如检查格式,例如使用regex,并根据哪个regex匹配,解析到正确的类型。我用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));
    }
}

相关问题