杰克逊在映射中捕获未识别的字段

4

我在Java Rest API中使用Jackson来处理请求参数。

我的Bean类:

public class ZoneModifBeanParam extends ModifBeanParam<Zone> {
@FormParam("type")
private String type;

@FormParam("geometry")
private Geometry geometry;

@FormParam("name")
private String name;

...

我的API接口:

@POST
@Consumes("application/json")
@Produces("application/json; subtype=geojson")
@ApiOperation(value = "Create a zone", notes = "To create a zone")
public Response createZone(ZoneModifBeanParam zoneParam) {

...

这个可以正常工作,但我需要在一个Map中接收一些没有在我的Bean中指定的其他参数。 例如:

{
   "geometry": {...},
   "name": "A circle name",
   "type": "4",
   "hello": true
}

接收到这个之后,我需要将“hello”和true作为一对键值存储在我的bean中声明的Map(名为unrecognizedFields)中。

是否有任何注解或对象可以实现这个功能?


您可以通过配置ObjectMapper来安全地忽略未识别的字段,但是如果要将它们作为键值对放入Map字段中,则需要自己编写反序列化器。 - Mena
2个回答

13

只需使用@JsonAnySetter。那就是它的用途所在。这里有一个测试案例。

public class JacksonTest {

    public static class Bean {
        private String name;
        public String getName() { return this.name; }
        public void setName(String name) { this.name = name; }

        private Map<String, Object> unrecognizedFields = new HashMap<>();

        @JsonAnyGetter
        public Map<String, Object> getUnrecognizedFields() {
            return this.unrecognizedFields;
        }

        @JsonAnySetter
        public void setUnrecognizedFields(String key, Object value) {
            this.unrecognizedFields.put(key, value);
        }
    }

    private final String json
            = "{\"name\":\"paul\",\"age\":600,\"nickname\":\"peeskillet\"}";
    private final ObjectMapper mapper = new ObjectMapper();

    @Test
    public void testDeserialization() throws Exception {
        final Bean bean = mapper.readValue(json, Bean.class);
        final Map<String, Object> unrecognizedFields = bean.getUnrecognizedFields();

        assertEquals("paul", bean.getName());
        assertEquals(600, unrecognizedFields.get("age"));
        assertEquals("peeskillet", unrecognizedFields.get("nickname"));
    }
}

@JsonAnyGetter 用于序列化方面。在将 bean 序列化时,您将不会在 JSON 中看到 unrecognizedFields。相反,映射中的所有属性将作为 JSON 中的顶级属性进行序列化。


这正是我正在寻找的!感谢您的帮助。 - Guinoutortue

2

您可能可以通过配置ObjectMapper来安全地忽略未识别的字段,但是要将它们具体放置为Map字段的键值对,您需要自己创建反序列化器。

这里有一个(大大简化的)示例:

给定您的POJO...

@JsonDeserialize(using=MyDeserializer.class)
class Foo {
    // no encapsulation for simplicity
    public String name;
    public int value;
    public Map<Object, Object> unrecognized;
}

...和您的自定义反序列化器...

(涉及IT技术)
class MyDeserializer extends JsonDeserializer<Foo> {
    @Override
    public Foo deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        // new return object
        Foo foo = new Foo();
        // setting unrecognized container
        Map<Object, Object> unrecognized = new HashMap<>();
        foo.unrecognized = unrecognized;
        // initializing parsing from root node
        JsonNode node = p.getCodec().readTree(p);
        // iterating node fields
        Iterator<Entry<String, JsonNode>> it = node.fields();
        while (it.hasNext()) {
            Entry<String, JsonNode> child = it.next();
            // assigning known fields
            switch (child.getKey()) {
                case "name": {
                    foo.name = child.getValue().asText();
                    break;
                }
                case "value": {
                    foo.value = child.getValue().asInt();
                    break;
                }
                // assigning unknown fields to map
                default: {
                    foo.unrecognized.put(child.getKey(), child.getValue());
                }
            }

        }
        return foo;
    }
}

那么,某个地方...
ObjectMapper om = new ObjectMapper();
Foo foo = om.readValue("{\"name\":\"foo\",\"value\":42,\"blah\":true}", Foo.class);
System.out.println(foo.unrecognized);

输出

{blah=true}

你的解决方案很好,我已经测试过了!但是 peeskillet 的解决方案更兼容我的表单注释。感谢你的帮助。 - Guinoutortue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接