springmvc:json

148 阅读4分钟

我是跟着b站狂神说的SpringMVC来进行学习的。这是我的笔记。www.bilibili.com/video/BV1aE…

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

JSON

在前后端分离时代:

后端部署后端,提供接口:

JOSN 是一个数据交换格式,是前后端数据交换格式。用来将数据传输

前端部署前端,负责渲染后端的数据

我理解的 JSON 是,前端只接收这种格式的数据,前端返回的格式也只有 JSON。所以后端需要有将数据变成 JSON 格式的方法和将 JSON 格式数据变成普通数据的方法。

JSON 的格式

我们可以通过 js 自带的 JSON 类来观察一下 JSON 的格式

【xml】

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>通过控制台看json格式</title>
    <script type="text/javascript">

        // 编写一个 js 对象
        let user = {
            name: "何鸭子",
            age: 21,
            sex: "男"
        };

        // 将对象之间送到前端
        console.log(user);

        console.log("=================================")

        // 将 js 对象转换为 json 对象
        let json = JSON.stringify(user);
        console.log(json);

        console.log("=================================")

        // 将 json 对象转换为 js 对象
        let parse = JSON.parse(json);
        console.log(parse)
    </script>
</head>
<body>
</body>
</html>

image-20220415160844304

  • 我们可以看出,直接的对象传输和 JSON 格式传输是有不同的。现在是通过 js 自带的方法对数据进行转换。后端也有类似的方法对数据进行转换。就是下面的 Jackson 和 fastjson 了。

Jackson 对象传输

我们可以通过 Jackson 来将后端的数据转换成 JSON 格式的对象。下面是我们将数据转换的操作步骤

  1. 第一步我们需要先导入依赖

    <!-- jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.2.2</version>
    </dependency>
    

    我们需要将依赖导入 lib 包中

    image-20220415162917149

  2. 创建一个 Controller 类。我们需要在类上添加 @RestController 注解。@RestController 写在类上,这个类的所有方法返回的字符串都不会经过视图解析器,而是变成普通字符串。

    image-20220415162536432

    如果前后端分离,我们的方法都不会走视图解析器了,只会返回 json 字符串到前端。让前端去对数据进行渲染。

    直接在类上写 @RestController 等同于 类上写@Controller + 方法@ResponseBody 的操作(表示这个方法是只返回字符串不走视图解析器)。一般都是使用前者。

  3. 在类中创建方法,通过Jackson 的 ObjectMapper 对象的 writeValueAsString 方法将转化成 json 格式的字符串

    @RequestMapping("/j2")
    public String json2() throws JsonProcessingException {
        // 1. 创建 Jackson 的 ObjectMapper 对象
        ObjectMapper mapper = new ObjectMapper();
    
        // 2. 创建对象
        User user = new User(1, "何鸭子", 21);
    
        // 3. 将对象转换成 json 格式,会抛出异常提示
        String s = mapper.writeValueAsString(user);
    
        return s;
    }
    

    如果以后熟练掌握了,想要传输 JSON 字符串,可以用 return new ObjectMapper().writeValueAsString(数据); 就可以了

    new ObjectMapper().writeValueAsString(user)
    

【结果】

image-20220415165353145

可以看到 JSON 中也有中文乱码的问题,我们接下来就两种方法来解决。


Jackson 乱码解决方法

方法一

方法一是直接使用 springmvc 注解来解决

@RequestMapping(value = "/j2", produces = "application/json;charset=utf-8")
public String json2() throws JsonProcessingException {
    // 1. 创建 Jackson 的 ObjectMapper 对象
    ObjectMapper mapper = new ObjectMapper();

    // 2. 创建对象
    User user = new User(1, "何鸭子", 21);

    // 3. 将对象转换成 json 格式,会抛出异常提示
    String s = mapper.writeValueAsString(user);

    return s;
}

image-20220415165905364

添加了 produces属性指定 JSON 的编码格式用 utf-8。

【结果】

image-20220415170158389

可以看到对象的所有属性和属性值都变成了 utf-8 的格式,就连双引号也变了。

方法二

我们可以在 springmvc 的配置文件上添加一段消息 StringHttpMessageConverter 转换配置

将下面的代码放入 springmvc 的配置文件

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

这是固定配置,可以写在模板里面。我们可以通过代码来测试一下

@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
    // 1. 创建 Jackson 的 ObjectMapper 对象
    ObjectMapper mapper = new ObjectMapper();

    // 2. 创建对象
    User user = new User(1, "何鸭子", 21);

    // 3. 将对象转换成 json 格式,会抛出异常提示
    String s = mapper.writeValueAsString(user);

    return s;
}

【结果】

image-20220415170643203

【总结】

这个方法只会对中文部分进行指定 utf-8 编码格式,方便前端接收;而不是像方法一一样将将整个 JSON 字符串都变成了 utf-8 的格式。我认为这个方法更好。


Jackson 集合传递

我们后端将数据传到前端一般都是以对象的方式传输。当多个对象的时候我们会使用集合。下面就来展示一下集合转化成 JSON 字符串之后是怎么样的。

@RequestMapping("/j4")
public String json4() throws JsonProcessingException {
    // 创建多个对象放在一个 json 字符串中传到前端去
    User user1 = new User(1, "何鸭子", 21);
    User user2 = new User(1, "何鸭子", 21);
    User user3 = new User(1, "何鸭子", 21);
    User user4 = new User(1, "何鸭子", 21);

    List<User> list = new ArrayList<>();
    list.add(user1);
    list.add(user2);
    list.add(user3);
    list.add(user4);

    return new ObjectMapper().writeValueAsString(list);
}

【结果】

image-20220415171152348

【总结】

我们可以看到用一个 '[ ]' 来表示集合。集合中不同的对象用 ',' 来间隔,用 '{ }' 来表示一个对象。对象里 用 " ":" " 来表示属性和属性值之间的关系。


Jackson 时间显示

我们将时间字符串变成 JSON 格式传到前端有两种方法

无论如何我们都是通过时间戳来将时间输出

@RequestMapping("/j5")
public String json5() throws JsonProcessingException {
    // 将 Date 类型的对象放在一个 json 字符串中传到前端去
    Date date = new Date();

    // 传到前端的是一个时间戳 Timestamp 1970.01.01到现在的毫秒数
    return new ObjectMapper().writeValueAsString(date);
}

【结果】

image-20220415172448807

方法一:使用 java 传统方法将时间输出

@RequestMapping("/j6")
public String json6() throws JsonProcessingException {
    // 使用 java 传统方法将时间输出
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    // 传到前端的是一个按照一定格式的时间
    return new ObjectMapper().writeValueAsString(sdf.format(date));
}

【结果】

image-20220415172503979

这个方法是用了 java 的传统方法将时间输出,算是复习了一下。

方法二:使用 Jackson 的 ObjectMapper 方法将时间输出

@RequestMapping("/j7")
public String json7() throws JsonProcessingException {
    // 使用 ObjectMapper 方法将时间输出

    ObjectMapper mapper = new ObjectMapper();
    //不使用时间戳的方式
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    //自定义日期格式对象
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //指定日期格式
    mapper.setDateFormat(sdf);

    Date date = new Date();

    return mapper.writeValueAsString(date);
}

【结果】

image-20220415172636195

这个方法调用了 Jackson 的方法,但是可以看出实现的步骤要比普通 java 方法要多,但是有许多步骤是和 Jackson 对象传输以及集合传递是相同的。所以我们可以通过写一个 Jackson 的方法类来将相同的步骤集合到一起。


自定义 JacksonUtils

image-20220415173341076

可以看出红色框和黄色框的操作都是相同的,而蓝色框是仅在时间输出的时候才需要执行。所以我们可以写一个工具类将重复的动作封装

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.text.SimpleDateFormat;

/**
 * @author workplace
 * @date 2022/4/14 22:49
 */
public class JsonUtils {
    public static String getJson(Object object) {
        return getJson(object, "YYYY-MM-DD hh:mm:ss");
    }

    public static String getJson(Object object, String dateFormat) {
        // 使用 ObjectMapper 方法将时间输出
        ObjectMapper mapper = new ObjectMapper();
        //不使用时间戳的方式
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //自定义日期格式对象
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        //指定日期格式
        mapper.setDateFormat(sdf);

        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}

【测试】

@RequestMapping("/j8")
public String json8() throws JsonProcessingException {
    // 使用 JsonUtils 方法将时间输出

    Date date = new Date();

    return JsonUtils.getJson(date);
}

image-20220415174923496

【测试】

@RequestMapping("/j9")
public String json9() throws JsonProcessingException {
    // 创建多个对象放在一个 json 字符串中传到前端去
    User user1 = new User(1, "何鸭子", 21);
    User user2 = new User(1, "何鸭子", 21);
    User user3 = new User(1, "何鸭子", 21);
    User user4 = new User(1, "何鸭子", 21);

    List<User> list = new ArrayList<>();
    list.add(user1);
    list.add(user2);
    list.add(user3);
    list.add(user4);

    return JsonUtils.getJson(list);
}

image-20220415175038392

【总结】

创建了 JsonUtils 工具类,对于需要重写的方法可以通过互相调用来减少代码的冗余。

我的理解:对于方法类重载,我们可以先写参数多的,再用参数少的去调用,将多余的参数定死。必须注意,所有的重载的方法的最终目的都是一样的,不能改变!


使用 fastjson

【导入依赖】

<!-- fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.80</version>
</dependency>

image-20220415174621226

【代码测试】

@RequestMapping("/j10")
public void json10() throws JsonProcessingException {
    // 创建多个对象通过 fastjson 创建一个 json 字符串中传到前端去


    // 写代码的时候一定要认真。如果出现问题必然不是代码出问题,而是环境没配置好。以后运维背锅就对了嘿嘿嘿

    //创建一个对象
    User user1 = new User(1, "何鸭子", 21);
    User user2 = new User(1, "何鸭子", 21);
    User user3 = new User(1, "何鸭子", 21);
    User user4 = new User(1, "何鸭子", 21);

    List<User> list = new ArrayList<>();
    list.add(user1);
    list.add(user2);
    list.add(user3);
    list.add(user4);

    System.out.println("*******Java对象 转 JSON字符串*******");
    String str1 = JSON.toJSONString(list);
    System.out.println("JSON.toJSONString(list)==>" + str1);
    String str2 = JSON.toJSONString(user1);
    System.out.println("JSON.toJSONString(user1)==>" + str2);

    System.out.println("\n****** JSON字符串 转 Java对象*******");
    User jp_user1 = JSON.parseObject(str2, User.class);
    System.out.println("JSON.parseObject(str2,User.class)==>" + jp_user1);

    System.out.println("\n****** Java对象 转 JSON对象 ******");
    JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
    System.out.println("(JSONObject) JSON.toJSON(user2)==>" + jsonObject1.getString("name"));

    System.out.println("\n****** JSON对象 转 Java对象 ******");
    User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
    System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>" + to_java_user);
}

【结果】

image-20220415174805094

可以尝试自己去写一下,继续完善自己的 JacksonUtils