springboot http序列化和反序列化HttpMessageConverter Converter 的优先级顺序

本文介绍 Spring Boot 中 HttpMessageConverter 的三种添加方式及其优先级顺序。通过实例展示了如何自定义 FastJsonHttpMessageConverter 并设置其在 List 中的位置,以实现不同格式数据的高效转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HttpMessageConverter  优先级

    // 添加converter的第一种方式,代码很简单,也是推荐的方式
    // 这样做springboot会把我们自定义的converter放在顺序上的最高优先级(List的头部)
    // 即有多个converter都满足Accpet/ContentType/MediaType的规则时,优先使用我们这个
    @Bean
    public MyMessageConverter converter()
    {
        System.out.println ("添加converter的第一种方式");
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        // 创建配置类
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.DisableCircularReferenceDetect
        );

        //此处是全局处理方式
        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        config.setCharset(Charset.forName("UTF-8"));
        fastConverter.setFastJsonConfig(config);

        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.ALL);
        supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        supportedMediaTypes.add(MediaType.APPLICATION_XML);
        fastConverter.setSupportedMediaTypes(supportedMediaTypes);
        //支持text 转string
        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
        //数据与javax.xml.transform.Source的相互转换
        SourceHttpMessageConverter sourceHttpMessageConverter = new SourceHttpMessageConverter ();

        //使用Jackson的XmlMapper转换XML数据
        Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
        builder.indentOutput(true);
        MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter = new MappingJackson2XmlHttpMessageConverter(builder.build());

        HttpMessageConverters httpMessageConverters = new HttpMessageConverters(mappingJackson2XmlHttpMessageConverter, sourceHttpMessageConverter, fastConverter,stringHttpMessageConverter );

        return  httpMessageConverters;
// 添加converter的第二种方式
    // 通常在只有一个自定义WebMvcConfigurerAdapter时,会把这个方法里面添加的converter(s)依次放在最高优先级(List的头部)
    // 虽然第一种方式的代码先执行,但是bean的添加比这种方式晚,所以方式二的优先级 大于 方式一
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        System.out.println ("添加converter的第二种方式");
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig config = new FastJsonConfig();
        //保留空的字段
        config.setSerializerFeatures(
                SerializerFeature.WriteNullListAsEmpty,//List null -> ""
                SerializerFeature.WriteNullStringAsEmpty,//String null -> ""
                SerializerFeature.WriteNullNumberAsZero,//Number null -> 0
                SerializerFeature.WriteMapNullValue);
        // 按需配置,更多参考FastJson文档哈

        //此处是全局处理方式
        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        config.setCharset(Charset.forName("UTF-8"));

        converter.setFastJsonConfig(config);
        converter.setDefaultCharset(Charset.forName("UTF-8"));
        //处理中文乱码问题
//        List<MediaType> fastMediaTypes = new ArrayList<>();
//        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//        converter.setSupportedMediaTypes(fastMediaTypes);
        converter.setSupportedMediaTypes(Arrays.asList(MediaType.ALL, MediaType.APPLICATION_JSON_UTF8,MediaType.APPLICATION_XML));
        converters.add(converter);

        // initialize your MappingJackson2XmlHttpMessageConverter
//        MappingJackson2XmlHttpMessageConverter xmlMessageConverter = new MappingJackson2XmlHttpMessageConverter();
//        xmlMessageConverter.setSupportedMediaTypes ();
//
//        //Here we add our custom-configured HttpMessageConverters
//        converters.add(new CustomObjectConverter(xmlMessageConverter));
//        super.configureMessageConverters(converters);
    }
    // 添加converter的第三种方式
    // 同一个WebMvcConfigurerAdapter中的configureMessageConverters方法先于extendMessageConverters方法执行
    // 可以理解为是三种方式中最后执行的一种,不过这里可以通过add指定顺序来调整优先级,也可以使用remove/clear来删除converter,功能强大
    // 使用converters.add(xxx)会放在最低优先级(List的尾部)
    // 使用converters.add(0,xxx)会放在最高优先级(List的头部)
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        System.out.println ("添加converter的第三种方式");
        // converters.add(new JavaSerializationConverter());
    }

 其中第一种方式 的 HttpMessageConverters httpMessageConverters = new HttpMessageConverters(mappingJackson2XmlHttpMessageConverter, sourceHttpMessageConverter, fastConverter,stringHttpMessageConverter );

看源码

 

这个是核心代码  决定Converter 的优先级顺序

private List<HttpMessageConverter<?>> getCombinedConverters(Collection<HttpMessageConverter<?>> converters, List<HttpMessageConverter<?>> defaultConverters) {
        List<HttpMessageConverter<?>> combined = new ArrayList();
        List<HttpMessageConverter<?>> processing = new ArrayList(converters);
        Iterator var5 = defaultConverters.iterator();

        while(var5.hasNext()) {
            HttpMessageConverter<?> defaultConverter = (HttpMessageConverter)var5.next();
            Iterator iterator = processing.iterator();

            while(iterator.hasNext()) {
                HttpMessageConverter<?> candidate = (HttpMessageConverter)iterator.next();
                if (this.isReplacement(defaultConverter, candidate)) {
                    combined.add(candidate);
                    iterator.remove();
                }
            }

            combined.add(defaultConverter);
            if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) {
                this.configurePartConverters((AllEncompassingFormHttpMessageConverter)defaultConverter, converters);
            }
        }

        combined.addAll(0, processing);
        return combined;
    }

参考 

https://www.cnblogs.com/page12/p/8168107.html

 

 

<think>好的,我现在要解决用户关于Newtonsoft.Json序列化时消息截断的问题。首先,我需要理解用户的问题到底是什么。用户提到在序列化时发生“消息截断”,这可能意味着生成的JSON字符串不完整,或者某些属性被省略了。我需要考虑可能的原因解决方法。 首先,我需要回忆一下Newtonsoft.Json(Json.NET)的常见配置可能导致数据截断的情况。可能的原因有几个方面: 1. **MaxDepth设置限制**:Json.NET默认的序列化深度是64层。如果用户序列化的对象嵌套层级过深,超过了这个限制,可能会被截断。这时候需要调整MaxDepth设置。 2. **循环引用处理**:如果对象之间存在循环引用,而设置中没有处理这种情况(比如设置ReferenceLoopHandling为Ignore),可能导致某些属性被忽略,看起来像截断。 3. **自定义JsonSerializerSettings**:用户可能使用了自定义的设置,比如忽略空值(NullValueHandling.Ignore)或者默认值(DefaultValueHandling.Ignore),导致部分属性未被序列化。 4. **字符串长度限制**:在某些环境中,比如ASP.NET的MaxJsonLength限制,当序列化特别大的对象时可能会被截断。但通常这个限制是在反序列化时由框架处理,而不是Json.NET本身的问题。 5. **内存或流处理问题**:如果使用流式写入时没有正确刷新或关闭流,可能导致数据不完整。或者在序列化过程中发生异常,导致输出不完整。 接下来,我需要逐一分析这些可能性,并提供相应的解决方法。 首先,检查MaxDepth设置。用户可能在序列化深度较大的对象时触发了默认限制。例如: ```csharp var settings = new JsonSerializerSettings { MaxDepth = 128 // 增加最大深度 }; string json = JsonConvert.SerializeObject(obj, settings); ``` 其次,循环引用的情况。如果对象A引用对象B,对象B又引用对象A,而设置中没有处理循环引用,可能会导致意外截断。可以配置: ```csharp settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; ``` 或者使用PreserveReferencesHandling: ```csharp settings.PreserveReferencesHandling = PreserveReferencesHandling.All; ``` 第三个可能性是忽略空值或默认值。检查是否有设置NullValueHandling或DefaultValueHandling为Ignore,导致某些属性未被序列化。例如: ```csharp settings.NullValueHandling = NullValueHandling.Include; // 包含空值 settings.DefaultValueHandling = DefaultValueHandling.Include; ``` 第四,如果是在ASP.NET应用程序中,可能需要调整MaxJsonLength属性,尤其是在返回JSON响应时。例如: ```csharp var result = new JsonResult(data); result.MaxJsonLength = int.MaxValue; return result; ``` 不过,这属于框架层面的配置,不是Json.NET本身的设置。 第五,流处理问题。如果使用JsonTextWriter或其他流式处理,确保正确关闭刷新流: ```csharp using (var writer = new StreamWriter("file.json")) using (var jsonWriter = new JsonTextWriter(writer)) { serializer.Serialize(jsonWriter, obj); // 不需要手动flush,因为using会处理 } ``` 另外,检查是否有异常被捕获,导致序列化未完成。比如在try-catch块中可能吞掉了异常,导致输出不完整。 此外,其他可能性包括自定义Converter出错,导致部分数据未正确处理。检查是否有自定义的JsonConverter,可能在转换过程中提前终止或抛出异常。 用户可能需要检查日志或调试以确认是否有异常发生。或者在序列化后检查json字符串的长度,判断是否确实被截断,或者是否有其他处理步骤导致截断。 总结可能的解决步骤: 1. 调整MaxDepth设置。 2. 处理循环引用。 3. 检查NullValueHandlingDefaultValueHandling。 4. 确保流正确关闭。 5. 检查自定义Converter。 6. 确认没有异常导致中断。 用户需要根据具体情况逐一排查这些可能性,找到导致截断的原因并应用相应的解决方案。</think>在Newtonsoft.Json(Json.NET)序列化过程中出现消息截断,通常是由于配置限制或对象结构问题导致部分数据未被完整序列化。以下是逐步排查解决问题的指南: --- ### **1. 检查序列化深度限制(MaxDepth)** **问题原因**: Json.NET默认最大序列化深度为64层。若对象嵌套层级超过此限制,超出的部分会被截断。 **解决方法**: 调整`MaxDepth`配置,适当增加深度限制。 ```csharp var settings = new JsonSerializerSettings { MaxDepth = 128 // 根据需求调整 }; string json = JsonConvert.SerializeObject(obj, settings); ``` --- ### **2. 处理循环引用** **问题原因**: 对象之间存在相互引用(如A→B→A),未配置循环引用处理时,可能导致部分属性被忽略。 **解决方法**: 通过以下配置忽略循环引用或保留引用信息: ```csharp // 忽略循环引用(直接跳过) settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // 或保留引用信息(添加$id/$ref标识) settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; ``` --- ### **3. 检查空值默认值处理** **问题原因**: 若配置了忽略空值或默认值,可能导致部分属性未被序列化,看起来像截断。 **解决方法**: 确保包含所有属性值: ```csharp settings.NullValueHandling = NullValueHandling.Include; // 包含空值 settings.DefaultValueHandling = DefaultValueHandling.Include; // 包含默认值 ``` --- ### **4. 环境限制(如ASP.NET的MaxJsonLength)** **问题原因**: 在ASP.NET应用中,默认的JSON响应长度限制可能导致截断。 **解决方法**: 调整`MaxJsonLength`(需在框架层面配置): ```csharp var result = new JsonResult(data); result.MaxJsonLength = int.MaxValue; // 设置为最大值 return result; ``` --- ### **5. 流处理或写入问题** **问题原因**: 使用流写入JSON时未正确关闭或刷新流,导致数据未完全写入。 **解决方法**: 确保正确释放资源: ```csharp using (var streamWriter = new StreamWriter("data.json")) using (var jsonWriter = new JsonTextWriter(streamWriter)) { JsonSerializer serializer = new JsonSerializer(); serializer.Serialize(jsonWriter, obj); // using块会自动调用FlushDispose } ``` --- ### **6. 检查自定义转换器(JsonConverter)** **问题原因**: 自定义的`JsonConverter`实现可能有逻辑错误,导致序列化提前终止。 **解决方法**: - 检查自定义转换器代码,确保正确处理所有分支。 - 尝试暂时移除自定义转换器,观察是否解决问题。 --- ### **7. 验证序列化结果** 通过以下步骤确认问题: 1. **捕获异常**:添加`try-catch`块,检查序列化是否抛出异常。 2. **日志输出**:记录序列化后的JSON长度,判断是否被截断。 3. **简化对象**:逐步简化对象结构,定位导致截断的属性。 --- ### **总结** 消息截断通常由配置限制或对象结构问题引起。按以下优先级排查: 1. 调整`MaxDepth`循环引用配置。 2. 检查空值/默认值处理。 3. 确保流正确关闭。 4. 验证环境限制(如ASP.NET的`MaxJsonLength`)。 5. 排查自定义转换器。 通过逐步调整配置简化对象,可有效定位并解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值