首页 > 解决方案 > Jackson ObjectMapper 在转换为 POJO 时会忽略某些键

问题描述

这是我的课

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.*;

public class Util {

    public static void main(String[] args) throws JsonProcessingException {
        String json = "{ \"argA\" : 5, \"unneededkey\" : 6}";
        ObjectMapper mapper = new ObjectMapper();
        MyObject object3 = mapper.readValue(json, MyObject.class);
        System.out.println(object3);

    }
    @ToString
    @RequiredArgsConstructor
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class MyObject {
        public MyObject(int argA) {
            this.argA = argA;
        }

        public MyObject(int argA, boolean argB) {
            this.argA = argA;
            this.argB = argB;
        }

        public MyObject(int argA, int argC, int argD) {
            this.argA = argA;
            this.argC = argC;
            this.argD = argD;
        }

        public MyObject(int argA, String argE) {
            this.argA = argA;
            this.argE = argE;
        }

        public int argA = 1;
        public boolean argB;
        public int argC = 4;
        public int argD = 5;
        public String argE;
    }
}

@JsonIgnoreProperties(ignoreUnknown = true) 有效,但这会忽略所有不需要的键。如果我只想忽略某些不需要的键,并且如果我得到一个不属于我的对象构造函数或在不需要的集合中的键,它会抛出异常。

标签: java

解决方案


我建议的方法:

@JsonIgnoreProperties(ignoreUnknown = true)将注释保留在您的班级上。

假设以下输入 JSON:

{
    "argA": 5,
    "unneededkey": 6,
    "unexpectedKey": 99
}

创建一个包含允许的字段名称的集合(其中“允许”是您的类中存在的字段MyObject,或者可以安全地忽略它):

Set<String> allowedFields = new HashSet(Arrays.asList(
            "argA", "argB", "argC", "argD", "argE",
            "unneededkey", "unneededkey2"));

然后对 JSON 进行如下处理:

ObjectMapper mapper = new ObjectMapper();

JsonNode rootNode = mapper.readTree(json);
Iterator<String> fieldNames = rootNode.fieldNames();
boolean fieldFailure = false;
Set<String> unexpectedFields = new HashSet();

while (fieldNames.hasNext()) {
    String fieldName = fieldNames.next();
    if (!allowedFields.contains(fieldName)) {
        fieldFailure = true;
        unexpectedFields.add(fieldName);
    }
}

然后,您可以根据布尔值选择如何进行fieldFailure。要么抛出异常(使用意外字段名称列表获取信息),要么以通常的方式创建对象:

MyObject object = mapper.readValue(json, MyObject.class);

不利的一面是它可能会遍历 JSON 两次——一次检查问题,再一次(假设没有问题)反序列化数据。

注意事项:

(1) 上面的代码假定一个扁平的 JSON 结构 - 没有嵌入对象 - 这与您在问题中的内容相匹配。为了获得更大的灵活性,您可以使用以下findKeys方法递归访问所有节点:

Map<String, Object> treeMap = mapper.readValue(json, Map.class);
List<String> keys  = Lists.newArrayList();
List<String> result = findKeys(treeMap, keys);

private List<String> findKeys(Map<String, Object> treeMap, List<String> keys) {
    treeMap.forEach((key, value) -> {
        if (value instanceof LinkedHashMap) {
            Map<String, Object> map = (LinkedHashMap) value;
            findKeys(map, keys);
        }
        keys.add(key);
    });

    return keys;
}

致谢:有关详细信息,请参阅此答案

(2) 我考虑了一个自定义反序列化器:

public class MyDeserializer extends StdDeserializer<Util.MyObject> { 
    ...
}

但是您仍然会遇到同样的问题 - 更不用说调用正确的构造函数所需的额外工作,具体取决于它找到的非空字段值的特定组合。


推荐阅读