序列化报错:com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token

在使用MQ消费消息的时候,系统突然大量报错:

[ERROR][SimpleAsyncTaskExecutor-2] log.Logger2File 事件消息接收异常: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: {"eventKey":"17","eventObject":{"operate":"METHOD_ADD","operateId":17}}; line: 1, column: 17] (through reference chain: EventTransferObject["eventObject"])
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: {"eventKey":"17","eventObject":{"operate":"METHOD_ADD","operateId":17}}; line: 1, column: 17] (through reference chain: EventTransferObject["eventObject"])
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
	at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:749)
	at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:59)
	at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:12)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:538)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:238)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3051)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2160)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:584)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:482)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:61)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:111)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:627)
	at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:454)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:480)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:464)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$300(SimpleMessageListenerContainer.java:61)
	at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:558)
	at java.lang.Thread.run(Thread.java:745)

导致这个问题的原因是发送端和接收端的对象属性格式或类型不一致。

看下面的错误demo

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;

import java.io.IOException;

public class Test28 {

    private static final ObjectMapper mapper = new ObjectMapper();

    public static void main(String[] args) {
        String str = "{\n" +
                "    \"id\": 2,\n" +
                "    \"name\": \"张三\",\n" +
                "    \"addressInfo\": {\n" +
                "        \"info\": \"中国\"\n" +
                "    }\n" +
                "}\n";

        JsonTestClass jsonTestClass = parseJSONObject(str, JsonTestClass.class);
        System.out.println(jsonTestClass.toString());

    }

    public static <T> T parseJSONObject(String content, Class<T> clazz) {
        try {
            return mapper.readValue(content, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

@Data
class JsonTestClass {
    private Integer id;
    private String name;
    private String addressInfo;
}

运行代码报错:

Exception in thread "main" java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
 at [Source: (String)"{
    "id": 2,
    "name": "张三",
    "addressInfo": {
        "info": "中国"
    }
}
"; line: 4, column: 20] (through reference chain: com.my.study.JsonTestClass["addressInfo"])
	at com.my.study.Test28.parseJSONObject(Test28.java:30)
	at com.my.study.Test28.main(Test28.java:21)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
 at [Source: (String)"{
    "id": 2,
    "name": "张三",
    "addressInfo": {
        "info": "中国"
    }
}

模拟出来的错误和我们上面写的是一致的。

那么如何解决呢?很简单,保证发送和接收端类型一致即可。

只需要修改JsonTestClass类的addressInfo属性的类型,从String类型,修改为能解析addressInfo-info这样结构的类型,这里我们定一个一个类,如下:

@Data
class JsonTestClass {
    private Integer id;
    private String name;
    private AddressInfo addressInfo;
}

@Data
class AddressInfo {
    private String info;
}

运行代码,输出如下:

JsonTestClass(id=2, name=张三, addressInfo=AddressInfo(info=中国))