Jackson configuration to consume list of records i

2019-07-10 22:58发布

问题:

I am using spring boot amqp in which I will be consuming a list of Employee objects from a queue. My listener method looks like this:

@RabbitListener(queues = "emp_queue")
public void processAndPortEmployeeData(List<Employee> empList) {
    empList.forEach(emp -> { some logic })
}

However, when I try to consume the message, I get a class cast exception: For some reason, I'm getting a LinkedHashMap.

Caused by: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.integration.domain.Employee

If I change my listener method to consume a single employee object, it works fine and I'm using the following jackson configurations for it:

@Configuration
@EnableRabbit
public class RabbitConfiguration implements RabbitListenerConfigurer {

@Bean
public MappingJackson2MessageConverter jackson2Converter() {
    return new MappingJackson2MessageConverter();
}

@Bean
public DefaultMessageHandlerMethodFactory handlerMethodFactory() {
    DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
    factory.setMessageConverter(jackson2Converter());
    return factory;
}

@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
    registrar.setMessageHandlerMethodFactory(handlerMethodFactory());
}

}

Is there some other jackson configuration that I need to do to consume the list of employee objects?

Thanks a lot!

Sample Input Json message which I will be consuming:

[
  {
    "name" : "Jasmine",
    "age" : "24",
    "emp_id" : 1344
  },
  {
    "name" : "Mark",
    "age" : "32",
    "emp_id" : 1314
  }
]

回答1:

What version of Spring AMQP are you using?

If 1.6 or greater, the framework passes the argument type to the message converter.

Before 1.6 you either need type information in the message headers, or you need to configure the converter with type information.

That said, since the converter created a map, it implies that was what received (not a list).

Please show a sample of the JSON in a message.

EDIT

Note that boot auto-configures the message converter if there's a single bean of that type...

@SpringBootApplication
public class So40491628Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So40491628Application.class, args);
        Resource resource = new ClassPathResource("data.json");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        FileCopyUtils.copy(resource.getInputStream(), baos);
        context.getBean(RabbitTemplate.class).send("foo", MessageBuilder.withBody(baos.toByteArray())
            .andProperties(MessagePropertiesBuilder.newInstance().setContentType("application/json").build()).build());
        Thread.sleep(10000);
        context.close();
    }

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

    @Bean
    public Queue foo() {
        return new Queue("foo");
    }

    @RabbitListener(queues = "foo")
    public void listen(List<Employee> emps) {
        System.out.println(emps);
    }

    public static class Employee {

        private String name;

        private String age;

        private int empId;

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAge() {
            return this.age;
        }

        public void setAge(String age) {
            this.age = age;
        }

        public int getEmpId() {
            return this.empId;
        }

        public void setEmpId(int empId) {
            this.empId = empId;
        }

        @Override
        public String toString() {
            return "Employee [name=" + this.name + ", age=" + this.age + ", empId=" + this.empId + "]";
        }

    }
}

Result:

[Employee [name=Jasmine, age=24, empId=0], Employee [name=Mark, age=32, empId=0]]