用于ArrayList内容的spring-rabbit JSON反序列化 [英] spring-rabbit JSON deserialization for ArrayList contents
问题描述
我正在将Spring-boot与Rabbitmq结合使用并进行JSON消息序列化.使用Direct-Reply-to功能的回复无法在java.util.List
容器内反序列化我的类.
I am using Spring-boot with rabbitmq with JSON message serialization. Replies using the Direct-Reply-to feature cannot deserialize my classes inside the java.util.List
container.
在Jackson2JsonMessageConverter.fromMessage()
中使用调试器,MessageProperties
指出__TypeID__
已正确设置为java.util.ArrayList
.但是__ContentTypeId__
是java.lang.Object
是不正确的,因为我期望FooDto(我假设...).
异常消息是:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to FooDto
Using my debugger in Jackson2JsonMessageConverter.fromMessage()
, the MessageProperties
states the __TypeID__
is correctly set to java.util.ArrayList
. However the __ContentTypeId__
is java.lang.Object
is incorrect as I would be expecting FooDto (I assume...).
The exception message is:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to FooDto
请注意,我使用的是Spring-rabbit 1.7.3,而不是v2.0,因此不能将ParameterizedTypeReference
用于rabbitTemplate.convertSendAndReceiveAsType()
方法.
Please note, I am using spring-rabbit 1.7.3 and not v2.0 so cannot use the ParameterizedTypeReference
for rabbitTemplate.convertSendAndReceiveAsType()
method.
我尝试使用DefaultClassMapper
和DefaultJackson2JavaTypeMapper
(在TYPE_ID
和INFERRED
两者下都经过TypePrecedence测试):
I have attempted to use the DefaultClassMapper
and the DefaultJackson2JavaTypeMapper
(with TypePrecedence tested under both TYPE_ID
and INFERRED
) without success:
private DefaultJackson2JavaTypeMapper classMapper() {
final DefaultJackson2JavaTypeMapper classMapper = new DefaultJackson2JavaTypeMapper();
final Map<String, Class<?>> idClassMapping = new HashMap<>();
idClassMapping.put(FooDto.class.getSimpleName(), FooDto.class);
classMapper.setIdClassMapping(idClassMapping);
return classMapper;
}
现在是例外:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to FooDto
到目前为止,我的解决方法是使用原始类型的数组,即FooDto[]
.
My workaround so far has been to use arrays of the raw types i.e. FooDto[]
.
库版本: -弹簧靴1.5.6 -RabbitMQ:3.7.4 -春季兔子1.7.3
Library versions: - spring-boot 1.5.6 - RabbitMQ: 3.7.4 - spring-rabbit 1.7.3
Maven pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
</dependencies>
有效Pom:
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>1.7.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.7.3.RELEASE</version>
</dependency>
RabbitMQ配置:
@Configuration
@EnableRabbit
public class MessagingConfiguration implements ShutdownListener {
// FIXME: List<FooDto> in the direct-reply response contains ArrayList<Object> due to __ContentTypeID__ == SimpleObject/Object . __TypeID__ is correctly ArrayList
@Bean
public List<Declarable> bindings() {
final List<Declarable> declarations = new ArrayList<>();
final FanoutExchange exchange = new FanoutExchange("fx", true, false);
final Queue queue = QueueBuilder.durable("orders").build();
declarations.add(exchange);
declarations.add(queue);
declarations.add(BindingBuilder.bind(queue).to(exchange));
return declarations;
}
// @Bean
// public DefaultClassMapper classMapper() {
// DefaultClassMapper classMapper = new DefaultClassMapper();
// Map<String, Class<?>> idClassMapping = new HashMap<>();
// idClassMapping.put("FooDto", FooDto.class);
// java.util.List<FooDto>
// classMapper.setIdClassMapping(idClassMapping);
// return classMapper;
// }
//
// @Bean
// public DefaultClassMapper classMapper() {
// final DefaultClassMapper typeMapper = new DefaultClassMapper();
// // typeMapper.setDefaultType(new ArrayList<FooDto>().getClass());
// typeMapper.setDefaultType(FooDto[].class);
// return typeMapper;
// }
@Bean
public Jackson2JsonMessageConverter jsonConverter() {
// https://stackoverflow.com/questions/40491628/jackson-configuration-to-consume-list-of-records-in-rabbitmq
// https://github.com/FasterXML/jackson-core/issues/295
final Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
converter.setTypePrecedence(TypePrecedence.TYPE_ID);
// converter.setClassMapper(classMapper());
return converter;
}
@ConditionalOnProperty(name = "consumer", havingValue = "true")
@Bean
public ConsumerListener listenerConsumer() {
return new ConsumerListener();
}
@ConditionalOnProperty(name = "producer", havingValue = "true")
@Bean
public ProducerListener listenerProducer() {
return new ProducerListener();
}
@Bean
public RabbitAdmin rabbitAdmin(final CachingConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
final ConnectionFactory connectionFactory) {
// Setting the annotation @RabbitListener to use Jackson2JsonMessageConverter
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jsonConverter());
factory.setConcurrentConsumers(5);
factory.setMaxConcurrentConsumers(5);
return factory;
}
@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonConverter()); // convert all sent messages to JSON
rabbitTemplate.setReplyTimeout(TimeUnit.SECONDS.toMillis(3));
rabbitTemplate.setReceiveTimeout(TimeUnit.SECONDS.toMillis(3));
return rabbitTemplate;
}
@Override
public void shutdownCompleted(final ShutdownSignalException arg0) {
}
}
在交换"fx"上,来自队列订单"的包含MyQuery对象的侦听器消费消息:
public class ConsumerListener {
private static final Logger log = LoggerFactory.getLogger(ConsumerListener.class);
@RabbitListener(queues = { "orders" })
public FooDto[] receiveMessage(final MyQuery query) {
log.info(query);
List<FooDto> response = new ArrayList<>();
response.add(new FooDto());
response.add(new FooDto());
response.add(new FooDto());
return response;
}
}
向Exchange发送邮件时使用的POJO: 类MyQuery { private String content ="test";
POJO used when sending a message to the Exchange: class MyQuery { private String content = "test";
public MyQuery();
public String toString() {
return content;
}
}
POJO用于响应: FooDto类{ private String content ="foo";
POJO used for the response: class FooDto { private String content = "foo";
public FooDto();
public String toString() {
return content;
}
}
推荐答案
您的听众有些奇怪;它的返回类型为void
,但是您返回一个列表.
There's something weird about your listener; it has a return type of void
but you return a list.
也就是说,我认为问题是由于类型擦除引起的.
That said, I think the problem is due to type erasure.
自定义ClassMapper
将无济于事,因为这仅适用于顶级课程.
A custom ClassMapper
won't help because that's just for a top-level class.
但是,您应该能够构建自定义的请参阅此处.
You should, however, be able to construct a custom Jackson2JavaTypeMapper to create a more complex type. The type mapper is consulted if there is not a class mapper. See here.
我现在不在电脑上,但是如果您不知道的话,明天我可以看一下.
I am not at a computer right now, but I can take a look tomorrow if you can't figure it out.
编辑
这是一个如何自定义转换器的示例...
Here's an example of how to customize the converter...
@SpringBootApplication
public class So49566278Application {
public static void main(String[] args) {
SpringApplication.run(So49566278Application.class, args);
}
@Bean
public ApplicationRunner runner(RabbitTemplate template) {
template.setReplyTimeout(60_000);
return args -> {
@SuppressWarnings("unchecked")
List<Foo> reply = (List<Foo>) template.convertSendAndReceive("so49566278", "baz");
System.out.println(reply);
Foo foo = reply.get(0);
System.out.println(foo);
};
}
@RabbitListener(queues = "so49566278")
public List<Foo> handle(String in) {
return Collections.singletonList(new Foo(in));
}
@Bean
public Queue queue() {
return new Queue("so49566278", false, false, true);
}
@Bean
public MessageConverter converter() {
Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();
converter.setJavaTypeMapper(new DefaultJackson2JavaTypeMapper() {
@Override
public JavaType toJavaType(MessageProperties properties) {
JavaType javaType = super.toJavaType(properties);
if (javaType instanceof CollectionLikeType) {
return TypeFactory.defaultInstance()
.constructCollectionLikeType(List.class, Foo.class);
}
else {
return javaType;
}
}
});
return converter;
}
public static class Foo {
private String bar;
public Foo() {
super();
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Override
public String toString() {
return "Foo [bar=" + this.bar + "]";
}
}
}
这篇关于用于ArrayList内容的spring-rabbit JSON反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!