使用 Jackson 对 List 进行自定义反序列化

问题描述 投票:0回答:4

我正在尝试编写一个自定义解串器,以便减少从其他地方收到的大量数据。我从反序列化器返回自定义对象列表。

我的问题是,如果这是我的自定义解串器,我该怎么做:

public class MyCustomDeserializer extends JsonDeserializer<List<CustomClass>> { ... }

我当然做不到:

final SimpleModule module = new SimpleModule();
module.addDeserializer(List<CustomClass>.class, new MyCustomDeserializer());

这样的东西会起作用吗?

final List<CustomClass> response = Arrays.asList(objectMapper.readValue(stringBean, CustomClass[].class));

如果这确实有效,我觉得有点混乱和“危险”?反序列化不是在 asList 方法调用中完成的吗?所以它基本上将 List 映射到 array[] ?

我了解了 TypeReference,所以我可能可以像这样使用它:

objectMapper.readValue(stringBean, new TypeReference<List<CustomClass>>(){});

但是我听说它比较慢。

我也不想为列表创建一个容器,并在反序列化中返回它,因为这意味着它将被包装在另一个 json 对象中,我只是希望我的端点生成类似的内容:

[{object1}, {object2}]

// instead of

{"Output" : [{object1}, {object2}]}

编辑:

我似乎误解了杰克逊在这两种情况下如何使用我的解串器:

final List<CustomClass> response = Arrays.asList(objectMapper.readValue(stringBean, CustomClass[].class));
// or
objectMapper.readValue(stringBean, new TypeReference<List<CustomClass>>(){});

看起来解串器被调用了两次,针对数组中的每个对象调用一次。我以为整个数组会被视为一个整体。为了消除混乱,我的意思是:

我收到并尝试反序列化的 json 看起来像这样:

[
  {
    "Data" : {
      "id" : "someId",
      "otherThing" : "someOtherThing"
    },
    "Message" : "OK"
  },
  {
    "Data" : null,
    "Message" : "Object not found for id blabla"
  }
]

所以我虽然这就是我的解串器中的内容,但正如我之前所说,似乎我实际上从该数组中获取了每个“条目”并多次调用它。

java json jackson deserialization
4个回答
17
投票

首先,如果您使用 bean

CustomClass
上的注释注册了自定义反序列化器,那么反序列化器应该处理
CustomClass
的一个实例,而不是一个集合,因此应该定义:

public class MyCustomDeserializer extends JsonDeserializer<CustomClass> {
        @Override
        public CustomClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException
        {
            ...
        }
}

现在您可以使用 Jackson 的类型工厂向映射器传递所需的类型信息

JavaType customClassCollection = objectMapper.getTypeFactory().constructCollectionType(List.class, CustomClass.class);
List<CustomClass> beanList = (List<CustomClass>)objectMapper.readValue(stringBean, customClassCollection);

10
投票

我通过向模型类中的属性添加自定义反序列化器并使用 JsonDeserialize 注释的

contentUsing()
方法来解决这个问题,如下所示:

@JsonDeserialize(contentUsing = MyCustomDeserializer.class)
private List<CustomClass> customClassObjList;

其中 MyCustomDeserializer 类是自定义 Jackson JSON 反序列化器,定义为:

public class MyCustomDeserializer extends JsonDeserializer<CustomClass> {
    
    @Override
    public CustomClass deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        ...
    }
}

2
投票

这两行就足够了。

ArrayNode arrayNode = (ArrayNode) objectMapper.readTree(stringBean);
List<CustomClass> response = objectMapper.convertValue(arrayNode, List.class); 

稍后谢谢我!


0
投票

也许您根本不需要自定义映射器。试试这个,也许它会帮助你继续理解 json 结构。

    public class Application {

    public static void main(String[] args) throws JsonProcessingException {
        String raw = "{\n" +
                "\"period\": [\"january\", \"FEBRUARY\", 3]\n" +
                "}";
        ObjectMapper mapper = new ObjectMapper();
        Data data = mapper.readValue(raw, Data.class);
        System.out.println("data = " + data);
        System.out.println("data.getMapped() = " + data.getMapped());


    }


    public static class Data {

        List<Object> period;

        public Data() {
        }

        public List<Object> getPeriod() {
            return period;
        }

        public void setPeriod(List<Object> period) {
            this.period = period;
        }

        public List<Month> getMapped() {
                return period.stream().map(o -> {
                    if(o instanceof String s){
                        return Month.getMe(s);
                    } else if(o instanceof Integer i){
                        return Month.getMe(i);
                    } else {
                        throw new RuntimeException("Unable to parse");
                }}).toList();
            }
        }


    enum Month {
        JANUARY(1, "January"),
        FEBRUARY(2, "February"),
        MARCH(3, "March"),
        APRIL(4, "April"),
        MAY(5, "May"),
        JUNE(6, "June"),
        JULY(7, "July"),
        AUGUST(8, "August"),
        SEPTEMBER(9, "September"),
        OCTOBER(10, "October"),
        NOVEMBER(11, "November"),
        DECEMBER(12, "December");

        public final int code;
        public final String lowerName;


        Month(int code,
              String lowerName) {
            this.code = code;
            this.lowerName = lowerName;
        }

        public static Month getMe(Integer i) {
            return Month.values()[i - 1];
        }

        public static Month getMe(String i) {
            return Arrays.stream(Month.values()).filter(e -> e.lowerName.equalsIgnoreCase(i))
                    .findAny().get();
        }
    }

}

考虑到类型是混合的,这里几乎不可能有一个优雅的解决方案,也许是从粗糙的解决方案中想到的最好的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.