繁体   English   中英

如何反序列化地图<String,Object>正确地在杰克逊

[英]How to Deserialize a Map<String,Object> correctly in Jackson

我有以下 POJO 可以序列化为 bytes 或json

public final class Message {

  private final Data data;
  private final Request request;
  private final Response response;

  public Message() {
    this.data = new Data();
    this.request = new Request();
    this.response = new Response();
  }

  public Data getData() {
    return data;
  }

  public Request getRequest() {
    return request;
  }

  public Response getResponse() {
    return response;
  }

  public Object query(String pointer) {
    return toJson().query(pointer);
  }

  public byte[] toBytes() {
    try {
      return new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(this);
    } catch (JsonProcessingException ex) {
      throw new MessageException(ex);
    }
  }

  public JSONObject toJson() {
    try {
      return new JSONObject(new ObjectMapper().writeValueAsString(this));
    } catch (JsonProcessingException ex) {
      throw new MessageException(ex);
    }
  }

  @Override
  public String toString() {
    try {
      return toString(0);
    } catch (MessageException ex) {
      throw new MessageException(ex);
    }
  }

  public String toString(int indent) {
    try {
      return toJson().toString(indent);
    } catch (MessageException ex) {
      throw new MessageException(ex);
    }
  }
}

参考类:

public class Data {

  private final Map<String, Map<String, Object>> dataMap;

  public Data() {
    this.dataMap = new HashMap();
  }

  public Data addToSet(String name, String key, Object value) {
    Map<String, Object> map = dataMap.get(name);
    if (map == null) {
      map = new HashMap();
    }
    map.put(key, value);
    dataMap.put(name, map);
    return this;
  }

  public Map<String, Map<String, Object>> getSets() {
    return dataMap;
  }

  public Data updateSet(String name, String key, Object value) {
    return Data.this.addToSet(name, key, value);
  }

  public Data removeFromSet(String name, String key) {
    Map<String, Object> map = dataMap.get(name);
    if (map == null) {
      throw new MessageException("No such property '" + key + "' for set '" + name + "'");
    }
    map.remove(key);
    return this;
  }

  public Map<String, Object> getSet(String name) {
    return dataMap.get(name);
  }
}
public class Request {

  private String method;
  private String resource;
  private final Map<String, Object> body;
  private final Map<String, String> headers;
  private final Map<String, String[]> parameters;

  public Request() {
    this.body = new HashMap();
    this.headers = new HashMap();
    this.parameters = new HashMap();
  }

  public String getMethod() {
    return Objects.toString(method, "");
  }

  public String getResource() {
    return Objects.toString(resource, "");
  }

  public Map<String, Object> getBody() {
    return body;
  }

  public Map<String, String> getHeaders() {
    return headers;
  }

  public Map<String, String[]> getParameters() {
    return parameters;
  }

  public String getHeader(String name) {
    return headers.get(name);
  }

  public Request setBody(String payload) {
    try {
      this.body.putAll(new ObjectMapper().readValue(payload, new TypeReference<Map<String, Object>>() {
      }));
      return this;
    } catch (JsonProcessingException ex) {
      throw new MessageException(ex);
    }
  }

  public Request setMethod(String name) {
    this.method = name;
    return this;
  }

  public Request setResource(String name) {
    this.resource = name;
    return this;
  }

  public Request setHeaders(Map<String, String> headers) {
    this.headers.putAll(headers);
    return this;
  }

  public Request setParameters(Map<String, String[]> parameters) {
    this.parameters.putAll(parameters);
    return this;
  }
}
public class Response {

  private String code;
  private String data;
  private String messageId;
  private String timestamp;
  private String description;

  public Response() {
  }

  public String getCode() {
    return Objects.toString(code, "");
  }

  public String getData() {
    return Objects.toString(data, "");
  }

  public String getMessageId() {
    return Objects.toString(messageId, "");
  }

  public String getTimestamp() {
    return Objects.toString(timestamp, "");
  }

  public String getDescription() {
    return Objects.toString(description, "");
  }

  public Response setCode(String code) {
    this.code = code;
    return this;
  }

  public Response setData(String data) {
    this.data = data;
    return this;
  }

  public Response setMessageId(String messageId) {
    this.messageId = messageId;
    return this;
  }

  public Response setTimestamp(String timestamp) {
    this.timestamp = timestamp;
    return this;
  }

  public Response setDescription(String description) {
    this.description = description;
    return this;
  }
}

序列化为 json 时,我得到一个有效的字符串

{
    "request": {
        "headers": {},
        "method": "",
        "resource": "",
        "body": {
            "whatsapp": {
                "conversationId": "39f09c41-1bd3-4e81-b829-babed3747d4b",
                "name": "Dave",
                "source": "+123456789098"
            },
            "payment": {
                "product": "chocolate",
                "amount": 1,
                "method": "cashapp",
                "msisdn": "123456789098",
                "entity": "The Fudge Shop"
            }
        },
        "parameters": {}
    },
    "data": {
        "sets": {
            "whatsapp": {
                "provider": "clickatell",
                "name": "Dave",
                "destination": "123456789098",
                "source": "123456789098",
                "message": "Your payment of $1.00 received, your receipt.no is QWJ124XPA9."
            },
            "cashapp": {
                "amount": 1,
                "receiptNo": "QWJ124XPA9",
                "name": "Dave Chapelle",
                "msisdn": "123456789098"
            }
        }
    },
    "response": {
        "code": "202",
        "data": "",
        "messageId": "20210623160202a647d32ee9ae477f9c90d8b1fbfd763a",
        "description": "Processing Request",
        "timestamp": "2021-06-23 16:02:02.408"
    }
}

当我尝试将 json 反序列化回 pojo 时

Message output = new ObjectMapper().readValue(json.toString(), Message.class);

我收到错误:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token

尝试反序列化Map<String, Object>主体时,错误似乎是从 Request 类生成的:

如何正确反序列化 Map?

对于字符串问题,这些来源可能会有所帮助:

无法从 START_OBJECT 令牌反序列化 java.lang.String 的实例

https://www.baeldung.com/jackson-map#1-mapltstring-stringgt-deserialization

为什么这个代码不能工作

杰克逊并不比你强大多少。

如果 Jackson 获得要序列化的对象,它会尝试序列化其所有值。 并且只有它的值(这对于类的独立性非常好)。 这是一个 json 对象:

{
"type":"apple",
"quantity":3,
"imageID":17
}

现在,这个对象的类是什么? 它可能是Fruit.classImage.class甚至RoundObject.class ,json 不知道,而 Jackson 也不知道。

那么json是如何找出类是什么的呢? 通过查看对象引用的类型。 在你的情况下,它是对象。 在 Object.class 中,Jackson 找不到需要已经保存的对象变量的构造函数,所以崩溃了。

解决方案

尝试序列化对象不是一个好主意。 如果您想放入非常不同的类,例如AppleBanana ,请创建一个名为Fruitinterfaceabstract class ,它们都实现了。 现在,在这个类的顶部使用这个注解:

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        property = "type") // name of the variable to save the kind of object you put in. NO VARIABLES in all classes that extend from Fruit are allowed to have this name (or at least @JsonProperty).
@JsonSubTypes({
        @JsonSubTypes.Type(value = Apple.class, name = "banana"),
        @JsonSubTypes.Type(value = Banana.class, name = "apple"),
})

使用 Map<String, Fr​​uit> 应该可以。

试试这个JacksonUtils

Message actual = createMessage();
String json = JsonUtils.prettyPrint().writeValue(actual);
System.out.println(json);
Message expected = JsonUtils.readValue(json, Message.class);

这是完整的片段:

public class MavenMain {
    public static void main(String... args) {
        Message actual = createMessage();
        String json = JsonUtils.prettyPrint().writeValue(actual);
        System.out.println(json);
        Message expected = JsonUtils.readValue(json, Message.class);
    }

    private static Message createMessage() {
        Message message = new Message();
        message.setData(createData());
        message.setRequest(createRequest());
        message.setResponse(createResponse());
        return message;
    }

    private static Data createData() {
        Map<String, Object> whatsapp = new LinkedHashMap<>();
        whatsapp.put("provider", "clickatell");
        whatsapp.put("name", "Dave");
        whatsapp.put("destination", "123456789098");
        whatsapp.put("source", "123456789098");
        whatsapp.put("message", "Your payment of $1.00 received, your receipt.no is QWJ124XPA9.");

        Map<String, Object> cashapp = new LinkedHashMap<>();
        cashapp.put("receiptNo", "QWJ124XPA9");
        cashapp.put("name", "Dave Chapelle");
        cashapp.put("msisdn", "123456789098");

        Map<String, Map<String, Object>> dataMap = new LinkedHashMap<>();
        dataMap.put("whatsapp", whatsapp);
        dataMap.put("cashapp", cashapp);

        Data data = new Data();
        data.setDataMap(dataMap);

        return data;
    }

    private static Request createRequest() {
        Map<String, Object> whatsapp = new LinkedHashMap<>();
        whatsapp.put("conversationId", "39f09c41-1bd3-4e81-b829-babed3747d4b");
        whatsapp.put("name", "Dave");
        whatsapp.put("source", "+123456789098");

        Map<String, Object> payment = new LinkedHashMap<>();
        payment.put("product", "chocolate");
        payment.put("amount", 1);
        payment.put("method", "cashapp");
        payment.put("msisdn", "123456789098");
        payment.put("entity", "The Fudge Shop");

        Map<String, Object> body = new HashMap<>();
        body.put("whatsapp", whatsapp);
        body.put("payment", payment);

        Request request = new Request();
        request.setHeaders(Collections.emptyMap());
        request.setMethod("");
        request.setResource("");
        request.setBody(body);
        request.setParameters(Collections.emptyMap());

        return request;
    }

    private static Response createResponse() {
        Response response = new Response();
        response.setCode("202");
        response.setData("");
        response.setMessageId("20210623160202a647d32ee9ae477f9c90d8b1fbfd763a");
        response.setDescription("Processing Request");
        response.setTimestamp("2021-06-23T16:02:02.408");
        return response;
    }
}

class Message {

    private Data data;
    private Request request;
    private Response response;

    public void setData(Data data) {
        this.data = data;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    public void setResponse(Response response) {
        this.response = response;
    }
}

class Data {

    @JsonProperty("sets")
    private Map<String, Map<String, Object>> dataMap;

    public void setDataMap(Map<String, Map<String, Object>> dataMap) {
        this.dataMap = dataMap;
    }
}

class Request {

    private String method;
    private String resource;
    private Map<String, Object> body;
    private Map<String, String> headers;
    private Map<String, String[]> parameters;

    public void setMethod(String method) {
        this.method = method;
    }

    public void setResource(String resource) {
        this.resource = resource;
    }

    public void setBody(Map<String, Object> body) {
        this.body = body;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public void setParameters(Map<String, String[]> parameters) {
        this.parameters = parameters;
    }
}

class Response {

    private String code;
    private String data;
    private String messageId;
    private String timestamp;
    private String description;

    public void setCode(String code) {
        this.code = code;
    }

    public void setData(String data) {
        this.data = data;
    }

    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }

    public void setTimestamp(String timestamp) {
        this.timestamp = timestamp;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

如果你想使用不可变对象,那么它是模型的另一种配置,但main类中的代码将是相同的。

对我@JsonDeserialize的解决方案是使用自定义反序列化、 @JsonDeserialize注释和JsonDeserializer接口,以达到预期的结果。

下面是解决方案:

public class Request {

  private String method;
  private String resource;
  @JsonDeserialize(using = BodyDeserializer.class)
  private final Map<String, Object> body;
  private final Map<String, String> headers;
  private final Map<String, String[]> parameters;

  public Request() {
    this.body = new HashMap();
    this.headers = new HashMap();
    this.parameters = new HashMap();
  }

  public String getMethod() {
    return method;
  }

  public String getResource() {
    return resource;
  }

  public Map<String, Object> getBody() {
    return body;
  }

  public Map<String, String> getHeaders() {
    return headers;
  }

  public Map<String, String[]> getParameters() {
    return parameters;
  }

  public String getHeader(String name) {
    return headers.get(name);
  }

  public Request setBody(Map<String, Object> body) {
    this.body.putAll(body);
    return this;
  }

  public Request setMethod(String name) {
    this.method = name;
    return this;
  }

  public Request setResource(String name) {
    this.resource = name;
    return this;
  }

  public Request setHeaders(Map<String, String> headers) {
    this.headers.putAll(headers);
    return this;
  }

  public Request setParameters(Map<String, String[]> parameters) {
    this.parameters.putAll(parameters);
    return this;
  }

  private static class BodyDeserializer extends JsonDeserializer<Map<String, Object>> {

    @Override
    public Map<String, Object> deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
      JsonDeserializer<Object> deserializer = dc.findRootValueDeserializer(dc.constructType(Map.class));
      Map<String, Object> map = (Map<String, Object>) deserializer.deserialize(jp, dc);
      return map;
    }
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM