【问题标题】:Mapping a dynamic json object field in Jackson?在杰克逊中映射动态 json 对象字段?
【发布时间】:2016-10-07 11:48:46
【问题描述】:

我在以下架构中有 json 对象:

{
  name: "foo",
  timestamp: 1475840608763,
  payload:
  {
     foo: "bar"
  }
}

这里payload字段包含一个内嵌的json对象,这个对象的schema是动态的,每次都不一样。

payload 对象是从不同 API 服务获得的原始输出,以及不同 API 服务的不同方法。不可能将其映射到所有可能的值。

是否可以有如下的java类:

public class Event
{
    public String name;
    public long timestamp;
    public JsonObject payload;
}

或者类似的东西,所以我可以接收基本架构并对其进行处理,然后将其发送到相关类,该类会将payload 转换为相应的预期类?

【问题讨论】:

  • 杰克逊 @JsonAnyGetter/Setter 是你的朋友 cowtowncoder.com/blog/archives/2011/07/entry_458.html
  • @Hector 在这种情况下,它会发送payload 作为我的 AnySetter 的键,并将 json 对象作为值,还是会调用 AnySetter 来获取有效负载的嵌套属性?跨度>
  • 你的 json 无效,(只是说)
  • 我认为@JsonAnyGetter 和@JsonAnySetter 没有必要。映射到JsonNode 就可以了。查看我的answer 了解更多详情。
  • 我认为 JsonNode 不是必需的 @JsonAnyGetter/Setter 确实可以解决问题。我不喜欢混合 Json“值对象”和 JsonNodes,不知何故它感觉不“正确”。

标签: java json jackson


【解决方案1】:

使用JsonNode

您可以使用 com.fasterxml.jackson.databind 包中的 JsonNode:

public class Event {

    public String name;
    public long timestamp;
    public JsonNode payload;

    // Getters and setters
}

然后使用解析它:

String json = "{\"name\":\"foo\",\"timestamp\":1475840608763,"
            + "\"payload\":{\"foo\":\"bar\"}}";

ObjectMapper mapper = new ObjectMapper();
Event event = mapper.readValue(json, Event.class);

将JsonNode 映射到 POJO

考虑一下,例如,您想将JsonNode 实例映射到以下类:

public class Payload {

    private String foo;

    // Getters and setters
}

可以通过以下代码实现:

Payload payload = mapper.treeToValue(event.getPayload(), Payload.class);

考虑Map<String, Object>

根据您的要求,您可以使用Map<String, Object> 代替JsonNode:

public class Event {

    public String name;
    public long timestamp;
    public Map<String, Object> payload;

    // Getters and setters
}

如果您需要将Map&lt;String, Object&gt; 转换为 POJO,请使用:

Payload payload = mapper.convertValue(event.getPayload(), Payload.class);

根据 Jackson documentation,convertValue() 方法在功能上类似于首先将给定值序列化为 JSON,然后将 JSON 数据绑定到给定类型的值,但应该更有效,因为完全序列化不需要(需要发生。但是,相同的转换器(序列化器和反序列化器)将用于数据绑定,这意味着相同的对象映射器配置工作。

【讨论】:

  • JsonNode 映射不适用于杰克逊 2.9 版
【解决方案2】:

这有帮助吗?

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;

public class Payload {

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

    @JsonAnyGetter
    public Map<String, Object> any() {
        return other;
    }

    @JsonAnySetter
    public void set(final String name, final Object value) {
        other.put(name, value);
    }

    public Map<String, Object> getOther() {
        return other;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + ((other == null) ? 0 : other.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Payload)) {
            return false;
        }
        Payload other = (Payload) obj;
        if (this.other == null) {
            if (other.other != null) {
                return false;
            }
        } else if (!this.other.equals(other.other)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Payload [other=" + other + "]";
    }

}

那么这个拥有类

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Outer {

    private final String name;
    private final long timestamp;

    private final Payload payload;

    @JsonCreator
    public Outer(@JsonProperty("name") final String name, @JsonProperty("timestamp") final long timestamp, @JsonProperty("payload") final Payload payload) {
        super();
        this.name = name;
        this.timestamp = timestamp;
        this.payload = payload;
    }

    public String getName() {
        return name;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public Payload getPayload() {
        return payload;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + ((name == null) ? 0 : name.hashCode());
        result = (prime * result) + ((payload == null) ? 0 : payload.hashCode());
        result = (prime * result) + (int) (timestamp ^ (timestamp >>> 32));
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Outer)) {
            return false;
        }
        Outer other = (Outer) obj;
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        if (payload == null) {
            if (other.payload != null) {
                return false;
            }
        } else if (!payload.equals(other.payload)) {
            return false;
        }
        if (timestamp != other.timestamp) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Outer [name=" + name + ", timestamp=" + timestamp + ", payload=" + payload + "]";
    }

}

然后去测试

public class Main {

    private static final ObjectMapper mapper = new ObjectMapper();

    public static void main(final String... args) throws JsonParseException, JsonMappingException, IOException {

        final Outer outer = mapper.readValue(new File("test.json"), Outer.class);

        System.out.println(outer);


    }

}

提供控制台输出

Outer [name=foo, timestamp=1475840608763, payload=Payload [other={foo=bar}]]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-10
    • 2013-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-15
    • 1970-01-01
    相关资源
    最近更新 更多