springboot系列十: 自定义转换器,处理JSON,内容协商

在这里插入图片描述


⬅️ 上一篇: springboot系列九: 接收参数相关注解


🎉 欢迎来到 springboot系列十: 自定义转换器,处理JSON,内容协商 🎉

在本篇文章中,我们将探讨如何在 Spring Boot 中实现自定义转换器、处理 JSON 数据以及进行内容协商。通过掌握这些技术,您可以更灵活地处理不同格式的数据,提高应用程序的兼容性和用户体验。


🔧 本篇需要用到的项目: springbootweb项目


自定义转换器

基本介绍

1.SpringBoot在响应客户端请求时, 将提交的数据封装成对象时, 使用了内置转换器

2.SpringBoot也支持自定义转换器, 这个内置转换器在debug的时候, 可以看到, 提供了124个内置转换器, 看下源码. GenericConverter类-ConvertiblePair(内部类)

在这里插入图片描述

在这里插入图片描述]

应用实例

需求说明: 演示自定义转换器使用

1.修改save.html

<!--使用自定义转换器关联car, 字符串整体提交, 使用,号间隔-->
坐骑: <input name="name" value="碧水金睛兽,666.6"><br/

2.创建src/main/java/com/zzw/springboot/config/WebConfig.java, 增加自定义转换器
springboot系列四: sprintboot容器功能

/**
 * @Configuration(proxyBeanMethods = false)
 * 1.表示 WebConfig 是一个配置类
 * 2.proxyBeanMethods = false 表示使用Lite模式
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig {

    //注入bean WebMvcConfiger
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {

        //整个是WebMvcConfigurer接口的匿名内部类
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {
                /**
                 * 解读
                 * 1.在addFormatters方法中, 增加一个自定义转换器
                 * 2.增加自定义转换器 String -> Car
                 * 3.增加的自定义转换器会注册到 converters 容器中
                 * 4.converters 底层结构是 ConcurrentHashMap, 内置有124个转换器[不同版本个数不一样~]
                 * 5.一会我们debug查看这些转换器
                 */
                registry.addConverter(new Converter<String, Car>() {//传入Converter接口的匿名内部类
                    @Override
                    public Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6
                        //这里加入自定义的转换业务代码
                        if (!ObjectUtils.isEmpty(source)) {
                            String[] split = source.split(",");
                            Car car = new Car();
                            car.setVehicleName(split[0]);
                            car.setVehiclePrice(Double.parseDouble(split[1]));
                            return car;
                        }
                        return null;
                    }
                });
            }
        };
    }
}

3.测试
monster = Monster(id=100, name=张三, age=30, maritalStatus=false, birthday=Sat Jan 01 00:00:00 CST 1994, car=Car(vehicleName=碧水金睛兽, vehiclePrice=666.6))

查看源码

1.debug, 可以看到我们新增的Converter

在这里插入图片描述
在这里插入图片描述

快捷键查看有多少元素

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

注意事项和细节

1.注册转换器换种写法, 方便理解

/**
 * @Configuration(proxyBeanMethods = false)
 * 1.表示 WebConfig 是一个配置类
 * 2.proxyBeanMethods = false 表示使用Lite模式
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig {

    //注入bean WebMvcConfiger
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {

        //整个是WebMvcConfigurer接口的匿名内部类
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {
                /**
                 * 解读
                 * 1.在addFormatters方法中, 增加一个自定义转换器
                 * 2.增加自定义转换器 String -> Car
                 * 3.增加的自定义转换器会注册到 converters 容器中
                 * 4.converters 底层结构是 ConcurrentHashMap, 内置有124个转换器[不同版本个数不一样~]
                 * 5.一会我们debug查看这些转换器
                 */
                /*registry.addConverter(new Converter<String, Car>() {//传入Converter接口的匿名内部类
                    @Override
                    public Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6
                        //这里加入自定义的转换业务代码
                        if (!ObjectUtils.isEmpty(source)) {
                            String[] split = source.split(",");
                            Car car = new Car();
                            car.setVehicleName(split[0]);
                            car.setVehiclePrice(Double.parseDouble(split[1]));
                            return car;
                        }
                        return null;
                    }
                });*/

                //换种写法注册自定义转换器, 方便理解

                //1.先创建一个自定义的转换器
                Converter<String, Car> zzwConverter = new Converter<String, Car>() {//传入Converter接口的匿名内部类
                    @Override
                    public Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6
                        //这里加入自定义的转换业务代码
                        if (!ObjectUtils.isEmpty(source)) {
                            String[] split = source.split(",");
                            Car car = new Car();
                            car.setVehicleName(split[0]);
                            car.setVehiclePrice(Double.parseDouble(split[1]));
                            return car;
                        }
                        return null;
                    }
                };
                //添加转换器到converters容器
                registry.addConverter(zzwConverter);

                //还可以增加更多的转换器....
            }
        };
    }
}

2.假如我们不添加自定义转换器, 会报typeMismatch错误, 报400错误. 而400错误是客户端的错误, 请求包含语法错误.
JavaWeb系列八: WEB 开发通信协议(HTTP协议)

在这里插入图片描述

3.创建两个自定义转换器

/**
 * @Configuration(proxyBeanMethods = false)
 * 1.表示 WebConfig 是一个配置类
 * 2.proxyBeanMethods = false 表示使用Lite模式
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig {

    //注入bean WebMvcConfiger
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {

        //整个是WebMvcConfigurer接口的匿名内部类
        return new WebMvcConfigurer() {
            @Override
            public void addFormatters(FormatterRegistry registry) {
                /**
                 * 解读
                 * 1.在addFormatters方法中, 增加一个自定义转换器
                 * 2.增加自定义转换器 String -> Car
                 * 3.增加的自定义转换器会注册到 converters 容器中
                 * 4.converters 底层结构是 ConcurrentHashMap, 内置有124个转换器[不同版本个数不一样~]
                 * 5.一会我们debug查看这些转换器
                 */
                /*registry.addConverter(new Converter<String, Car>() {//传入Converter接口的匿名内部类
                    @Override
                    public Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6
                        //这里加入自定义的转换业务代码
                        if (!ObjectUtils.isEmpty(source)) {
                            String[] split = source.split(",");
                            Car car = new Car();
                            car.setVehicleName(split[0]);
                            car.setVehiclePrice(Double.parseDouble(split[1]));
                            return car;
                        }
                        return null;
                    }
                });*/

                //换种写法注册自定义转换器, 方便理解

                //1.先创建一个自定义的转换器
                Converter<String, Car> zzwConverter = new Converter<String, Car>() {//传入Converter接口的匿名内部类
                    @Override
                    public Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6
                        //这里加入自定义的转换业务代码
                        if (!ObjectUtils.isEmpty(source)) {
                            String[] split = source.split(",");
                            Car car = new Car();
                            car.setVehicleName(split[0]);
                            car.setVehiclePrice(Double.parseDouble(split[1]));
                            return car;
                        }
                        return null;
                    }
                };

                //还可以增加更多的转换器....
                //第2个自定义的转换器
                Converter<String, Monster> zzwConverter2 = new Converter<String, Monster>() {
                    @Override
                    public Monster convert(String source) {
                        return null;
                    }
                };

                //添加转换器到converters容器
                registry.addConverter(zzwConverter);
                registry.addConverter(zzwConverter2);
            }
        };
    }
}

debug, 看一看 converters容器 是不是变成了 126 个.

在这里插入图片描述
在这里插入图片描述

4.创建三个自定义转换器, 由于key是[源类型->目标类型], 所以会覆盖掉一个.

//1.先创建一个自定义的转换器
Converter<String, Car> zzwConverter = new Converter<String, Car>() {//传入Converter接口的匿名内部类
    @Override
    public Car convert(String source) {//source就是 传入的字符串 碧水金睛兽,666.6
        //这里加入自定义的转换业务代码
        if (!ObjectUtils.isEmpty(source)) {
            String[] split = source.split(",");
            Car car = new Car();
            car.setVehicleName(split[0]);
            car.setVehiclePrice(Double.parseDouble(split[1]));
            return car;
        }
        return null;
    }
};

//还可以增加更多的转换器....
//第2个自定义的转换器
Converter<String, Monster> zzwConverter2 = new Converter<String, Monster>() {
    @Override
    public Monster convert(String source) {
        return null;
    }
};
//第3个自定义的转换器
Converter<String, Car> zzwConverter3 = new Converter<String, Car>() {
    @Override
    public Car convert(String source) {
        return null;
    }
};

//添加转换器到converters容器
registry.addConverter(zzwConverter);
registry.addConverter(zzwConverter2);
registry.addConverter(zzwConverter3);

1)测试, 是否覆盖.

在这里插入图片描述
在这里插入图片描述

2)查看 converters 容器. 因为第三个转换器和第一个转换器 key 是相同的, 所以覆盖掉了.

在这里插入图片描述

处理JSON

需求说明

演示返回JSON格式的数据

应用实例

1.SpringBoot 支持返回 JSON 格式数据, 在启用WEB开发场景时, 已经引入了相关依赖.
springboot系列二: sprintboot依赖管理

在这里插入图片描述
在这里插入图片描述

2.新建src/main/java/com/zzw/springboot/controller/ResponseController.java

@Controller
public class ResponseController {
    //编写方法, 返回monster数据 要求以json格式返回
    @GetMapping(value = "/getMonster")
    @ResponseBody
    public Monster getMonster() {
        //说明
        //开发中 monster对象是从db获取,这里我们模拟一个mosnter对象
        Monster monster = new Monster();
        monster.setId(100);
        monster.setName("张三");
        monster.setAge(18);
        monster.setBirthday(new Date());
        monster.setMaritalStatus(false);
        Car car = new Car();
        car.setVehicleName("奔驰");
        car.setVehiclePrice(100000.0);
        monster.setCar(car);
        return monster;
    }
}

3.Postman测试.

在这里插入图片描述

4.Debug一下 monster对象如何以Json格式返回.

浏览器/Postman 请求, 第一个断点

在这里插入图片描述

第二个断点, 找到 AbstractJackson2HttpMessageConverter.class

在这里插入图片描述

在这里插入图片描述

用工厂模式创建了个 generator.

在这里插入图片描述

generator是 UTF8JsonGenerator

在这里插入图片描述

object 就是 monster对象

在这里插入图片描述

这条语句一旦执行完毕, 浏览器就拿到数据.

在这里插入图片描述

内容协商

基本介绍

1.根据客户端接收能力不同, SpringBoot返回不同媒体类型的数据.
JavaWeb系列八: WEB 开发通信协议(HTTP协议)

2.比如:
客户端Http请求, 携带 Accept aaplication/xml 请求头, 要求服务端返回xml数据;
客户端Http请求, 携带 Accept aaplication/json 请求头, 则要求服务端返回json数据

3.效果如下
如果Accept, 我设置的是 application/json, 那么返回的数据就是 json 格式.
在这里插入图片描述

如果Accept, 我设置的是 application/xml, 那么返回的数据就是 xml 格式.

在这里插入图片描述

应用实例

需求说明: 使用Postman发送Http请求, 根据请求头不同, 返回对应的json数据, 或者xml数据, 如图

在这里插入图片描述

注意: Accept: */* 默认返回 json 格式

在这里插入图片描述

在底层,generator生成的是xml格式的, 但是在进行转换的时候, 需要有一个jar包的依赖.

<!--引入处理xml的依赖-->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.12.4</version>
</dependency>

debug源码

Postman切换不同的Accept类型, 来Debug源码, 看看对应的JsonGenerator类型

1,返回json类型

在这里插入图片描述

contentType进行内容协商

在这里插入图片描述

在这里插入图片描述

2.返回xml类型

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

优先返回xml

加入xml依赖以后, 使用浏览器请求,为什么会返回xml数据, 而不是json?

分析
(1)浏览器请求后, 后端接收到的contentType值是 application/xhtml+xml, 为什么?

(2)因为请求头信息, 如下
1.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8

2.application/xhtml+xml 的权重比较高0.9, 后面的类型, 包括 */* 的权重是 0.8

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注意事项和细节

1.Postman可以通过修改Accept的值, 来访会不同的数据格式.

2.对于浏览器, 我们无法修改其Accept的值, 怎么办? 解决方案: 开启基于请求参数的内容协商功能.

1)修改application.yml, 开启基于请求参数的内容协商功能.

spring:
  mvc:
    contentnegotiation:
      favor-parameter: true #开启基于请求参数的内容协商功能

在这里插入图片描述

2)浏览器测试

在这里插入图片描述

3)注意, 参数format是规定好的, 在开启请求参数的内容协商功能后, SpringBoot底层ParameterContentNegotiationStrategy会通过format来接收参数, 然后返回对应的媒体类型/数据格式, 当然format=xx这个xx 媒体类型/数据格式 是SpringBoot可以处理的才行, 不能乱写.

在这里插入图片描述

4)修改parameterName

spring:
  mvc:
    contentnegotiation:
      favor-parameter: true #开启基于请求参数的内容协商功能
      parameter-name: helloFormat #指定一个内容协商的参数名

5)测试

在这里插入图片描述


🔜 下一篇预告: [即将更新,敬请期待]


📚 目录导航 📚

  1. springboot系列一: springboot初步入门
  2. springboot系列二: sprintboot依赖管理
  3. springboot系列三: sprintboot自动配置
  4. springboot系列四: sprintboot容器功能
  5. springboot系列五: springboot底层机制实现 上
  6. springboot系列六: springboot底层机制实现 下
  7. springboot系列七: Lombok注解,Spring Initializr,yaml语法
  8. springboot系列八: springboot静态资源访问,Rest风格请求处理
  9. springboot系列九: 接收参数相关注解
  10. springboot系列十: 自定义转换器,处理JSON,内容协商

💬 读者互动 💬
在学习 Spring Boot 自定义转换器、处理 JSON 及内容协商的过程中,您有哪些新的发现或疑问?欢迎在评论区留言,让我们一起讨论吧!😊


Spring Boot 是一个基于 Spring 的轻量级框架,可以快速搭建基于 Spring 的应用程序。而 Fastjson 是一个高性能的 Java 序列化和反序列化库,可以处理复杂的 JSON 数据。 如果想要在 Spring Boot自定义 Fastjson 作为 JSON 消息转换器,可以按照以下步骤进行操作: 首先,在 pom.xml 文件中引入 Fastjson 的依赖,可以通过以下代码来添加依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.78</version> </dependency> ``` 然后,在 Spring Boot配置类中配置 Fastjson 作为 JSON 消息转换器。可以通过以下代码来实现: ```java @Configuration public class FastjsonConfig { @Bean public HttpMessageConverters fastjsonHttpMessageConverter() { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); converter.setDefaultCharset(Charset.forName("UTF-8")); List<MediaType> supportedMediaTypes = new ArrayList<>(); supportedMediaTypes.add(MediaType.APPLICATION_JSON); converter.setSupportedMediaTypes(supportedMediaTypes); FastJsonConfig config = new FastJsonConfig(); config.setSerializerFeatures(SerializerFeature.PrettyFormat); converter.setFastJsonConfig(config); return new HttpMessageConverters(converter); } } ``` 最后,在 Spring Boot 的主类中加上 @EnableWebMvc 注解,启用自定义JSON 消息转换器。 通过以上步骤,就成功地使用了 Spring Boot 自定义 Fastjson 作为 JSON 消息转换器。这样可以方便地处理 JSON 数据,提升了系统的性能和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~ 小团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值