当前位置: 首页 > 知识库问答 >
问题:

针对ArrayList内容的spring rabbit JSON反序列化

阎嘉荣
2023-03-14

我使用Spring Boot与Rabbitmq与JSON消息序列化。使用直接回复功能的回复不能反序列化java.util.List容器中的类。

Jackson2JsonMessageConverter中使用我的调试器。从message()MessageProperties说明\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>已正确设置为java。util。ArrayList。然而,\uuu ContentTypeId\uujava。lang.Object不正确,因为我希望FooDto(我假设…)。异常消息是:java。ClassCastException:java。util。LinkedHashMap无法强制转换为FooDto

请注意,我使用的是Spring-Rabbit 1.7.3而不是v2.0,所以不能使用ParameterizedType参考RabbitTemplate.convertSendAndReceiveAsType()方法。

我尝试使用DefaultClassMapperDefaultJackson2JavaTypeMapper(在TYPE\u ID推断的下测试了类型优先级),但没有成功:

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.ClassCastExcture:java.util.LinkedHashMap不能强制转换为FODto

到目前为止,我的解决方法是使用原始类型的数组,即FooDto[]

库版本:-spring boot 1.5.6-RabbitMQ:3.7.4-spring rabbit 1.7.3

Mavenpom.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) {
    }
}

侦听器正在使用exchange“fx”上队列“orders”中包含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”;

    public MyQuery();

    public String toString() {
        return content;
    }
}

POJO用于响应:class foDto{私有字符串内容="foo";

    public FooDto();

    public String toString() {
        return content;
    }
}

共有2个答案

薛欣德
2023-03-14

这种方式与收藏没有问题。我觉得很简单:https://stackoverflow.com/a/67130566/10746857

柳景胜
2023-03-14

你的听众有些奇怪;它的返回类型为void,但您返回一个列表。

也就是说,我认为问题是由于类型擦除。

自定义的ClassMapper不会有帮助,因为这只是针对顶级类。

但是,您应该能够构造一个定制的Jackson2JavaTypeMapper来创建一个更复杂的类型。如果没有类映射器,则会咨询类型映射器。看这里。

我现在不在电脑前,但如果你想不出来,我明天可以看一看。

编辑

下面是一个如何自定义转换器的示例。。。

@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 + "]";
        }

    }

}
 类似资料:
  • 假设序列化。bin是一个充满单词的文件,在序列化时是ArrayList 我希望能够反序列化“serialise.bin”文件并将内容存储在ArrayList中

  • 问题内容: 我试图序列化和反序列化内部对象的数组列表: HairList对象也是一个可序列化的对象。 此代码执行返回以下错误: 排队 我不知道我在做什么错。你能给个小费吗? 更新: 解决: 仅使用HairBirt的本机数组而不是ArrayList即可工作: 代替 感谢大家的帮助。 问题答案: 不要使用-而是使用二进制数据并对它进行base64编码,以将其转换为字符串而不会丢失信息。 我强烈怀疑这是

  • 我试图序列化一个对象数组,并将其写入一个名为address.ser的文件,然后从该文件中读取,反序列化对象数组并显示其属性。我尝试一次序列化整个arrayList(读取时在单个会话中反序列化它),也尝试一个接一个地序列化对象数组的每个对象(读取时一个接一个地反序列化它)。问题是,当从address.ser文件读回来时,我只得到最后一个被写入的对象的数据,而不是其他的。 以下是代码片段: 这是用于将

  • 试图将JSON反序列化为我创建的Java bean。对杰克逊和这项努力来说真的很新鲜,所以请容忍我。我有以下几点: 由于JSON中的对象被反复使用,我的Java Bean被分解为几个部分,因此: ...和以类似方式实现的另一个BlockBean类(为简洁起见省略)。我使用杰克逊来完成这个任务,我的问题是——杰克逊中是否有序列化和反序列化的机制?理想情况下,我想要这样的东西(下面是伪代码,因为我无法

  • 问题内容: 我有一堂课POJO 我有一个像 我正在使用Jackson ObjectMapper进行反序列化。在不创建任何其他父类的情况下如何获得? 如果不可能,是否有可能获得仅包含json字符串的第一个元素的对象,即在这种情况下和? 问题答案: 您首先需要获取数组 打印(带有)

  • 问题内容: 每当我尝试序列化文件时,都会收到错误消息:FileNotFound。不知道为什么。这是我的FileHelper代码: 问题答案: