首页 > 解决方案 > 杰克逊序列化:包含正则表达式的字符串字段反转义以获取实际文字

问题描述

我有一个 DTO,String其中包含一个包含正则表达式的类型字段。要成为有效的Java字符串,它包含double \,因为第一个用于转义,这很容易理解。喜欢:

    private final String regex = "myapp\\.\\w{2,3}\\/confirmation(.*)"; 

要使用的实际正则表达式是myapp\.\w{2,3}\/confirmation(.*).

而且,我将在 Kafka 消息中发送这个 DTO,序列化由 Jackson 完成。

ProducerRecord<String, String> record = new ProducerRecord<>(
    kafkaTopicProperties.getTopic(),
    String.valueOf(myDto.getOrderId()),
    objectMapper.writeValueAsString(myDto)
);

可以理解的是,Jackson 无法区分普通字符串和正则表达式字符串,并将按原样发送转义的 Java 字符串。此外,在 JSON 中省略转义也是无效的(至少在我编辑.json文件删除转义\时,IntelliJ 显示解析错误),因此对于有效的 JSON,我还需要对其进行转义。正常到现在。

但是,Kafka 的消费者将收到一个转义的正则表达式字符串,并且必须对正则表达式进行反转义(删除多余的\)。问题来了。句法变化导致语义差异。

实际上,因为 Kafka 对发送的内容没有限制,所以我们可以在发送之前自由地进行转义,因为它是纯文本。

但是,杰克逊能为我做这个魔术吗?

标签: javajsonjackson

解决方案


感谢@Bohemian 和@NyamiouTheGaleanthrope

确实如您所说,我想我发现了问题:反序列化器应该org.springframework.kafka.support.serializer.JsonDeserializer在 中Consumer,而在生产者中,序列化器应该在org.springframework.kafka.support.serializer.JsonSerializer. 那么一切都很好,我可以在日志中看到正则表达式没有额外的转义字符。我String(De)Serializer以前用过。

双方的配置放在一起:

application.yml

spring:
  kafka:
    producer:
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
    consumer: # would be picked if you autowired the consumer
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      properties.spring.json.trusted.packages: '*'

制片人:

ProducerRecord<String, OrderMessageDto> record = new ProducerRecord<>(
    kafkaTopicProperties.getTopic(),
    String.valueOf(orderDto.getId()),
    orderDto
);
kafkaTemplateOrder.send(record).get(kafkaTopicProperties.getTimeout(), TimeUnit.MILLISECONDS);

消费者(我只有一个消费者在测试,所以我必须手动配置):

    @Autowired
    private EmbeddedKafkaBroker kafkaBroker;

    ...


    private ConsumerRecords consumeRecords(String topic) {
        Map<String, Object> consumerProps = KafkaTestUtils.consumerProps(topic, "true", kafkaBroker);
        consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        JsonDeserializer<OrderPaymentUrlDto> valueDeserializer = new JsonDeserializer<>();
        valueDeserializer.addTrustedPackages("*"); // necessary for include DTO as trusted type
        ConsumerFactory<String, OrderPaymentUrlDto> factory = new DefaultKafkaConsumerFactory<>(
                consumerProps,
                new StringDeserializer(),
                valueDeserializer
        );
        Consumer consumer = factory.createConsumer();
        kafkaBroker.consumeFromAnEmbeddedTopic(consumer, topic);
        return KafkaTestUtils.getRecords(consumer, 2000);
    }

推荐阅读