com.fasterxml.jackson.databind.exc.MismatchedInputException: не удается десериализовать экземпляр `java.lang.Строка` из токена START_OBJECT

#java #spring #spring-boot #rabbitmq #json-deserialization

#java #spring #spring-boot #rabbitmq #json-десериализация

Вопрос:

Я использую Spring Boot и Rappitmq, чтобы реализовать асинхронный обмен сообщениями между двумя разными приложениями. Это работает нормально, когда я обмениваюсь строками только в качестве содержимого сообщения. Я также реализовал обмен pojo. Из-за того, что pojos находятся в разных пакетах, процесс сериализации / десериализации завершается с ошибкой. Чтобы решить эту проблему, я использую JacksonConverter для использования json. Это работает, как я вижу в заголовке сообщения в службе подписки «application / json».

Модель для обмена (то же самое в службе публикации и подписчика):

 public class Employee{

private Integer id;
private String firstName;
private String lastName;
private Double salary;

public Employee(@JsonProperty("id") Integer id,
                @JsonProperty("firstName") String firstName,
                @JsonProperty("lastName") String lastName,
                @JsonProperty("salary") Double salary) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.salary = salary;
}

public Integer getId() {
    return id;
}

public void setId(Integer id) {
    this.id = id;
}

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

public String getLastName() {
    return lastName;
}

public void setLastName(String lastName) {
    this.lastName = lastName;
}

public Double getSalary() {
    return salary;
}

public void setSalary(Double salary) {
    this.salary = salary;
}

@Override
public String toString() {
    return "{"  
            "id="   id  
            ", firstName='"   firstName   '''  
            ", lastName='"   lastName   '''  
            ", salary="   salary  
            '}';
}
  

Компоненты для использования Jackson2JsonConverter в службе publisher:

  @SpringBootApplication
public class PublisherApplication {

public static void main(String[] args) {

    SpringApplication.run(PublisherApplication.class, args);

}

@Bean
public Jackson2JsonMessageConverter producerMessageConverter(){
    return new Jackson2JsonMessageConverter();
}

@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory){
    RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
    rabbitTemplate.setMessageConverter(producerMessageConverter());
    return rabbitTemplate;
}
  

}

Отправить сообщение:

     @Override
public void sendEmployeeObject(Employee employee) throws JsonProcessingException {
    rabbitTemplate.convertAndSend(RabbitMqConstants.EXCHANGE_NAME, "foo.bar.baz", employee);
}
  

}

Конфигурация подписчика:

    @Configuration
public class EmployeeMessageSubscriberConfig {

@Bean
@Qualifier("employeeQueue")
Queue queue() {
    return new Queue(RabbitMqConstants.EMPLOYEE_QUEUE, false);
}

@Bean
@Qualifier("employeeExchange")
TopicExchange exchange() {
    return new TopicExchange(RabbitMqConstants.EXCHANGE_NAME);
}

@Bean
@Qualifier("employeeQueueExchangeBinding")
Binding binding(Queue queue, TopicExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#");
}
@Bean
public Jackson2JsonMessageConverter converter() {
    return new Jackson2JsonMessageConverter();
}
  

}

Метод прослушивателя:

  @RabbitListener(queues = RabbitMqConstants.EMPLOYEE_QUEUE)
    public void handleMessage(Employee employee) {

        System.out.println("Received <"   employee.toString()   ">");
    }
  

Чтобы проверить это, я отправляю объект Employee через rest api в службу publisher. Здесь я просто передаю его без изменений в rabbit. Служба подписчиков считывает сообщение и просто выводит его на консоль.

Проблема: Служба подписчиков печатает правильное значение:

 Received <{id=1, firstName='John', lastName='Doe', salary=1.5}>
  

но я также получаю трассировку ошибки:

     Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token
 at [Source: (String)"{"id":1,"firstName":"John","lastName":"Doe","salary":1.5}"; line: 1, column: 1]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) 
  

Итак, насколько я понимаю, подписчик видит, что входящий Json является объектом типа ob, но он пытается десериализовать его в виде строки.

Вопрос: Почему выводится правильный вывод, когда трассировка говорит, что, похоже, произошла ошибка? Когда я использую RestController, входящий объект автоматически сопоставляется с заданной моделью. Должен ли я каким-то образом подключать это при использовании rabbitmq?

Редактировать:

Это руководство, которому я следовал.

Как я видел в заголовке моего подписчика, есть поле headers={__TypeId__=com.publisher.model.Employee} поэтому я установил модель Employee в обоих приложениях для пакета с одинаковым именем. Все та же ошибка. (Как я и ожидал, потому что в отличие от standardMessageConverter имя пакета не используется для сериализации)

Комментарии:

1. Вы должны взять as json в качестве строки в handleMessage и сопоставить jsonString с Employee внутри

2. Вы уверены? Мне кажется, что десериализация объекта Json в Pojos является одним из наиболее распространенных вариантов использования. Я подумал, что использование Json также является преимуществом, поскольку его можно десериализовать и на других языках, не привязываясь к конкретному материалу Java.

3. @MedTech попробуйте с помощью @Payload annotations … тогда handleMessage должно понравиться это общедоступное сообщение с пустой обработкой (сообщение сотрудника @Payload)

4. @DineshKrishnan ничего не изменилось.

5. @MedTech попробуйте convertAndSend() метод публикации сообщения. Так что это должно быть rabbitTemplate.convertAndSend(exchangeName, routingkey, messageObject);

Ответ №1:

Очевидно, что вы не можете получить сообщение с помощью java-компонента напрямую.

попробуйте использовать byte[] msg или String msg в качестве аргумента для вашего handleMessage() метода и преобразовать его в свой Employee компонент вручную.

Если вы хотите получить свое сообщение с помощью java-компонента, конфигурация поможет.

 @Configuration
public class RabbitMQConfig {
    
    @Bean
    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        return factory;
    }

}
  

Комментарии:

1. Да, но, как я уже сказал, правильное значение также напечатано. Так что, похоже, это работает каким-либо образом?

2. Я только что обновил свой ответ. Вам просто нужна конфигурация

3. Я добавил этот компонент, но ничего не изменилось. Я больше всего зациклился на том, почему печатается правильное значение вместе с трассировкой ошибки, что его нельзя десериализовать. У вас есть источник для этого? Я уже думал, что это может быть проблема с аннотацией @RabbitListener

4. попробуйте использовать @Payload для аргумента employee