【问题标题】:How to customize SpringWebFlux WebClient JSON deserialization?如何自定义 SpringWebFlux WebClient JSON 反序列化?
【发布时间】:2017-10-01 19:57:23
【问题描述】:

我正在使用spring-webflux WebClient (build 20170502.221452-172) 来访问一个生成 Entry 对象流 (application/stream+json) 的 Web 应用程序,如下所示:

final WebClient producerClient = WebClient.create("http://localhost:8080/");

Flux<Entry> entries = producerClient.get().uri("json-stream")
        .accept(MediaType.APPLICATION_STREAM_JSON)
        .exchange()
        .flatMapMany(clientResponse -> clientResponse.bodyToFlux(Entry.class));

虽然 Entry 对象的反序列化对于使用标准常见类型(包括 java.time.Instant 等 Java 时间 (JSR-310) 数据类型)的 POJO 工作得很好,但我想知道我需要做什么为了将任何自定义 JSON 添加到 Java 反序列化(例如,自定义 Jackson ObjectMapper)。

我在WebClient 或其构建器和流式API 生成的对象的类中找不到任何API。

有人用过自定义反序列化的 WebClient 吗?

(也许 API 还没有?)

【问题讨论】:

    标签: java json spring reactive-programming spring-webflux


    【解决方案1】:

    这是一个为 JSON(反)序列化自定义 ObjectMapper 的示例。 请注意,出于流媒体目的,使用了不同的编码器/解码器,但其配置原理保持不变。

        ExchangeStrategies strategies = ExchangeStrategies
                .builder()
                .codecs(clientDefaultCodecsConfigurer -> {
                    clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
                    clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
    
                }).build();
    
        WebClient webClient = WebClient.builder().exchangeStrategies(strategies).build();
    

    【讨论】:

    • 您能说出Jackson2ObjectMapperBuilderCustomizer 不适用于默认编解码器的原因吗?
    • 这对我来说听起来像是一个新问题——你能创造一个吗?
    • @hahn 如果您使用 spring 提供的预配置 WebClient.Builder 而不是 Webclient.builder(),您可以自动完成 objectMapper 自定义参考:docs.spring.io/spring-boot/docs/current/reference/html/…
    【解决方案2】:

    您可以为特定的 WebClient 进行配置:

    @Autowired
    public ItunesAlbumServiceImpl(ObjectMapper mapper) {
        ExchangeStrategies strategies = ExchangeStrategies.builder().codecs(clientCodecConfigurer ->
            clientCodecConfigurer.customCodecs().decoder(
                    new Jackson2JsonDecoder(mapper,
                            new MimeType("text", "javascript", StandardCharsets.UTF_8)))
        ).build();
    
        webClient = WebClient.builder()
                .exchangeStrategies(strategies)
                .baseUrl("https://itunes.apple.com")
                .build();
    }
    

    但也适用于“应用程序级别”

    通过配置CodecCustomizer:

    @Bean
    public CodecCustomizer jacksonLegacyJsonCustomizer(ObjectMapper mapper) {
        return (configurer) -> {
            MimeType textJavascript = new MimeType("text", "javascript", StandardCharsets.UTF_8);
            CodecConfigurer.CustomCodecs customCodecs = configurer.customCodecs();
            customCodecs.decoder(
                    new Jackson2JsonDecoder(mapper, textJavascript));
            customCodecs.encoder(
                    new Jackson2JsonEncoder(mapper, textJavascript));
        };
    }
    

    这将由WebClientAutoConfiguration 作为WebClient.Builder bean 生效:

    @Autowired
    public ItunesAlbumServiceImpl(WebClient.Builder webclientBuilder) {
        webClient = webclientBuilder.baseUrl("https://itunes.apple.com").build();
    }
    

    【讨论】:

      【解决方案3】:

      根据上面的回复,我最终得到了以下代码:

      final ObjectMapper mapper = new ObjectMapper()
          .findAndRegisterModules()
          .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
      final ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
          .codecs(configurer -> configurer.defaultCodecs()
          .jackson2JsonDecoder(new Jackson2JsonDecoder(mapper)))
          .build();
      final WebClient webClient = WebClient.builder()
          .exchangeStrategies(exchangeStrategies)
          .build();
      

      如果你不包含.findAndRegisterModules(),那么当你想反序列化Java 8 的时间对象之类的东西时就会遇到问题。

      【讨论】:

        【解决方案4】:

        使用 webflux 5.0.2,取消注册默认值

        val strategies = ExchangeStrategies.builder()
                        .codecs { configurer ->
                            configurer.registerDefaults(false)
                            configurer.customCodecs().encoder(Jackson2JsonEncoder(objectMapper, APPLICATION_JSON))
                            configurer.customCodecs().decoder(Jackson2JsonDecoder(objectMapper, APPLICATION_JSON))
                        }.build()
        

        【讨论】:

        • 这个问题被标记为Java 而不是Kotlin
        【解决方案5】:

        全局配置:

        @Configuration
        public class AppConfig {
        
            private final ObjectMapper objectMapper;
        
            @Autowired
            public AppConfig(ObjectMapper objectMapper) {
                this.objectMapper = objectMapper;
                this.webClientBuilder = WebClient.builder()
                        .exchangeStrategies(exchangeStrategies());
            }
        
            private ExchangeStrategies exchangeStrategies() {
                Jackson2JsonEncoder encoder = new Jackson2JsonEncoder(objectMapper);
                Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(objectMapper);
                return ExchangeStrategies
                        .builder()
                        .codecs(configurer -> {
                            configurer.defaultCodecs().jackson2JsonEncoder(encoder);
                            configurer.defaultCodecs().jackson2JsonDecoder(decoder);
                        }).build();
            }
        }
        

        【讨论】:

          【解决方案6】:

          从 Spring 5.1.13 开始,您可以使用专用的 .codec 方法来自定义它们:

          WebClient.builder()
              .codecs(configurer -> {
                  configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
                  configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
               })
              .build();
          

          【讨论】:

          • 谢谢你 - 我真的应该用更新的 Spring 版本的代码更新这个问题......
          猜你喜欢
          • 2019-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-12
          • 2013-01-18
          • 2014-08-02
          • 2022-09-23
          • 1970-01-01
          相关资源
          最近更新 更多