将 JSON 反序列化为 class 实例
Deserialize JSON to class instance
我有一个 class 正在通过 RabbitMQ 作为消息发送,在发件人服务上,它的定义如下:
public final class User implements Serializable {
private String nome;
private String cognome;
public User(@JsonProperty("nome") String nome,
@JsonProperty("cognome") String cognome) {
this.nome = nome;
this.cognome = cognome;
}
public String getNome() {
return nome;
}
public String getCognome() {
return cognome;
}
public User(){}
}
在接收方服务上:
@Document
public class Persona {
@Id
@JsonProperty
public ObjectId id;
private String nome;
private String cognome;
public String getId() {
return id.toHexString();
}
public void setId(ObjectId id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getCognome() {
return cognome;
}
public void setCognome(String cognome) {
this.cognome = cognome;
}
public Persona(ObjectId id, String nome, String cognome) {
this.id = id;
this.nome = nome;
this.cognome = cognome;
}
public Persona(){}
}
在接收器控制器中,我有以下方法,它应该获取该消息,将其转换为一个对象,并将其保存在数据库中,它看起来像:
@RabbitListener(queues = {"default_parser_q"})
public void receiveMessage(final Message message){
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
mapper.readValue(message.getBody(), Persona.class);
System.out.println(message.toString() + "the message has been received");
}
问题是我在 readValue()
方法上遇到异常,特别是:
Unhandled exceptions: java.io.IOException, com.fasterxml.jackson.core.JsonParseException, com.fasterxml.jackson.databind.JsonMappingException
在这种情况下发送的消息 (JSON) 是:
{
'nome': "John",
'cognome': "Doe"
}
我做错了什么?
编辑:按要求添加堆栈跟踪。
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of demo.com.fetcherservice.models.Persona
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{
"nome": "John",
"cognome": "Doe"
}')
at [Source: (byte[])""{\n\t\t\t\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"\n\t\t}""; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3091)
at kdmforce.com.fetcherservice.services.FetcherService.receiveMessage(FetcherService.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120)
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:50)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:196)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:129)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1552)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:870)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:854)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access00(SimpleMessageListenerContainer.java:78)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1137)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1043)
at java.lang.Thread.run(Thread.java:748)
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of demo.com.fetcherservice.models.Persona
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{
"nome": "John",
"cognome": "Doe"
}')
at [Source: (byte[])""{\n\t\t\t\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"\n\t\t}""; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3091)
at kdmforce.com.fetcherservice.services.FetcherService.receiveMessage(FetcherService.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120)
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:50)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:196)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:129)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1552)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:870)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:854)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access00(SimpleMessageListenerContainer.java:78)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1137)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1043)
at java.lang.Thread.run(Thread.java:748)
@Edit3: 终于找到了匹配的输入,导致您发布的堆栈跟踪
问题是您对 jackson 映射器的输入是这样的:
\"{\n\t\t\t\\"nome\\": \\"John\\",\n\t\t\t\\"cognome\\": \\"Doe\\"\n\t\t}\"
杰克逊认为它是一个单一的价值,但未能将其映射到创作者。
正确的输入应该是这样的
{\n\t\t\t\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"\n\t\t}
正如 @daniu
发布的那样,这可能是由于其他地方的一些 Spring
干扰。
您可能可以编写复杂的自定义 JSON 配置,但最简单的解决方案是将您的消息反序列化回 User
class,然后向您的 Persona
class 以 id
和 User
class 作为参数
它看起来像我在 Kafka 中看到的行为,因为你也在使用 Spring 我认为它是相同的。
您的发送端将对象转换为 Json,然后将其作为字符串发送 - 因此错误消息中的转义引号 (\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"
)。
您需要在发送方声明 JsonSerializer,然后将其传递给您的 User
,或者 - 如果您手动创建 Json 字符串 - 声明它是一个字节数组所以 Spring 不会尝试转义引号和空格。
您似乎正在 "double" 编码 JSON。
如果您使用 RabbitTemplate
发送 JSON 字符串,您不应该使用 JSON 消息转换器,因为它会重新编码已经编码的 [=24] =].
要么使用 template.send(msg)
(messageProperties.contentType()
设置为 application/json
),要么,如果您使用 convertAndSend()
,请在模板中使用 SimpleMessageConverter
,然后
template.convertAndSend(exchange, rk, myJsonStrng, msg -> {
msg.getMessageProperties().setContentType("application/json");
return msg;
});
我有一个 class 正在通过 RabbitMQ 作为消息发送,在发件人服务上,它的定义如下:
public final class User implements Serializable {
private String nome;
private String cognome;
public User(@JsonProperty("nome") String nome,
@JsonProperty("cognome") String cognome) {
this.nome = nome;
this.cognome = cognome;
}
public String getNome() {
return nome;
}
public String getCognome() {
return cognome;
}
public User(){}
}
在接收方服务上:
@Document
public class Persona {
@Id
@JsonProperty
public ObjectId id;
private String nome;
private String cognome;
public String getId() {
return id.toHexString();
}
public void setId(ObjectId id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getCognome() {
return cognome;
}
public void setCognome(String cognome) {
this.cognome = cognome;
}
public Persona(ObjectId id, String nome, String cognome) {
this.id = id;
this.nome = nome;
this.cognome = cognome;
}
public Persona(){}
}
在接收器控制器中,我有以下方法,它应该获取该消息,将其转换为一个对象,并将其保存在数据库中,它看起来像:
@RabbitListener(queues = {"default_parser_q"})
public void receiveMessage(final Message message){
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
mapper.readValue(message.getBody(), Persona.class);
System.out.println(message.toString() + "the message has been received");
}
问题是我在 readValue()
方法上遇到异常,特别是:
Unhandled exceptions: java.io.IOException, com.fasterxml.jackson.core.JsonParseException, com.fasterxml.jackson.databind.JsonMappingException
在这种情况下发送的消息 (JSON) 是:
{
'nome': "John",
'cognome': "Doe"
}
我做错了什么?
编辑:按要求添加堆栈跟踪。
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of
demo.com.fetcherservice.models.Persona
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{ "nome": "John", "cognome": "Doe" }') at [Source: (byte[])""{\n\t\t\t\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"\n\t\t}""; line: 1, column: 1] at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343) at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032) at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371) at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373) at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3091) at kdmforce.com.fetcherservice.services.FetcherService.receiveMessage(FetcherService.java:23) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171) at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120) at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:50) at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:196) at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:129) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1552) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:870) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:854) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access00(SimpleMessageListenerContainer.java:78) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1137) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1043) at java.lang.Thread.run(Thread.java:748) com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance ofdemo.com.fetcherservice.models.Persona
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{ "nome": "John", "cognome": "Doe" }') at [Source: (byte[])""{\n\t\t\t\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"\n\t\t}""; line: 1, column: 1] at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63) at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343) at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032) at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371) at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373) at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161) at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3091) at kdmforce.com.fetcherservice.services.FetcherService.receiveMessage(FetcherService.java:23) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:171) at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:120) at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:50) at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:196) at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:129) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1552) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461) at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:870) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:854) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access00(SimpleMessageListenerContainer.java:78) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1137) at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1043) at java.lang.Thread.run(Thread.java:748)
@Edit3: 终于找到了匹配的输入,导致您发布的堆栈跟踪
问题是您对 jackson 映射器的输入是这样的:
\"{\n\t\t\t\\"nome\\": \\"John\\",\n\t\t\t\\"cognome\\": \\"Doe\\"\n\t\t}\"
杰克逊认为它是一个单一的价值,但未能将其映射到创作者。
正确的输入应该是这样的
{\n\t\t\t\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"\n\t\t}
正如 @daniu
发布的那样,这可能是由于其他地方的一些 Spring
干扰。
您可能可以编写复杂的自定义 JSON 配置,但最简单的解决方案是将您的消息反序列化回 User
class,然后向您的 Persona
class 以 id
和 User
class 作为参数
它看起来像我在 Kafka 中看到的行为,因为你也在使用 Spring 我认为它是相同的。
您的发送端将对象转换为 Json,然后将其作为字符串发送 - 因此错误消息中的转义引号 (\"nome\": \"John\",\n\t\t\t\"cognome\": \"Doe\"
)。
您需要在发送方声明 JsonSerializer,然后将其传递给您的 User
,或者 - 如果您手动创建 Json 字符串 - 声明它是一个字节数组所以 Spring 不会尝试转义引号和空格。
您似乎正在 "double" 编码 JSON。
如果您使用 RabbitTemplate
发送 JSON 字符串,您不应该使用 JSON 消息转换器,因为它会重新编码已经编码的 [=24] =].
要么使用 template.send(msg)
(messageProperties.contentType()
设置为 application/json
),要么,如果您使用 convertAndSend()
,请在模板中使用 SimpleMessageConverter
,然后
template.convertAndSend(exchange, rk, myJsonStrng, msg -> {
msg.getMessageProperties().setContentType("application/json");
return msg;
});