前后端数据传输格式(下)
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
上篇主要复习了HTTP以及POST相关的几种传参形式,这一篇来讲讲和实际开发更为紧密的内容:JSON。
初学者在需求分析阶段通常缺乏以下能力:
- 无法通过看页面原型图分析出大致的请求格式和响应格式
- 即使能分析出请求格式,却不知道用什么样的Java对象接收
- 即使能分析出响应格式,却不知道用什么样的Java对象返回

这三个能力,其实都依赖于你对JSON的理解(本文主要讨论JSON,不涉及表单请求和文件上传类型)。
说得更具体些就是,初学者往往搞不清楚JSON与Java对象的转换关系(图片来自尚硅谷):
- 请求:JSON转Java
- 响应:Java转JSON

JSON
JSON的作用
在我刚入行时写过相关的博客,比较简单,感兴趣的同学可以看看:AJAX与JSON
JSON简单来说就是特定格式的字符串,注意,它的载体是字符串。很多人分不清JSON和JS对象,其实两者没有可比性。实在要说的话,JS对象和Java对象都是正儿八经的对象,存活于内存中(浏览器/服务器),而JSON只是字符串,往往承担的是网络传输的角色:

我们都知道,网页上的动态数据都是服务器那边组装后通过HTTP返回的,但很少有人会思考这样的一个问题:
服务器组装数据时用的是Java对象,比如user.setName("bravo"),但前端得到的却是JS对象,怎么做到的?
一般来说,浏览器被安装在我们的个人电脑中,而提供服务的Java应用则可能运行在阿里云服务器上,两者并不在同一片物理内存。由于对象存活必须依赖于内存(如果不做持久化,只要一断电,内存中的数据就没了),所以就上图而言,Java对象显然没法自己“跳出”服务器进入浏览器中,更别提Java对象如何变成JS对象。
要想完成跨内存的数据传输、对象转换,必须通过网络传输,而且需要一个传递信息的载体,过程中还涉及到序列化和反序列化。
实际上,整个请求响应的过程就是序列化和反序列化的过程:
- 服务器端把Java对象序列化为JSON(中介,特定格式的字符串)
- 网络不能直接传递对象,但可以传输字符串
- 浏览器得到JSON后,反序列化为JS对象,然后设置到页面上

同理,不仅是浏览器和服务器,服务器和服务器之间也需要JSON作为数据交互的中介:

JSON的格式
使用Postman时,我们经常会发这样的请求:

Body就是请求体内容,一般都是字符串格式,所以这是一个JSON,而不是JS对象,尽管它们看起来很像。你可以理解为JSON是多种语言共同协定的一种数据交互格式:
- "field":"xxx" 表示对象的字段,value如果是字符类型需要加"",数值可以不加
- {} 表示对象或map或其他符合key-value格式的结构
- [] 表示一组对象、一组字符串或一组数值
很简单吧?
然后各个语言都会遵守这个协定,转化为自己的对象结构,比如:
- {}可以代表Java对象/Map,[]由于表示一组数据,刚好可以对应Java的数组、List或Set等单列集合
- {}也可以代表Python对象/字典,[]对应Python的元组或list等
- {}还可以代表PHP的对象,[]对应PHP中的Array
- ...
当然,{}也可以代表JS对象,[]则可以转化为JS的数组。
为什么很多初学者会搞混JSON和JS对象呢?本质上还是因为:
- 初学者都“见过”JS对象,而且它往往都是{}的形式出现
- JS对象的格式和JSON确实比较像
但JSON本质是“干瘪瘪”的字符串,当各大语言需要进行反序列化时,就会按照上面的格式转为内存中“圆鼓鼓”的对象。应该把JS对象和Java对象看作一个梯队,而JSON则在另一个梯队,是一个特定格式的字符串。
常见JSON格式与Java对象的转换
这里只演示Java对象与JSON的转换:

如果不信的话,可以自己动手写一下接口,然后用Postman按照图中的格式发送JSON,看看接口能不能顺利接收参数。
上面说过,JSON的{}可以对应Java的对象或者Map,{}两个括号表示对象的边界,其实刚好对应类的{},里面的就是对象的字段。
我们来分析一下第一张图的结构。
接口返回的是一个HashMap,所以很容易想到最终JSON格式是:
{
...
}
接着往Map里put了一些value,我们先不管value是什么类型:
{
"1号男嘉宾" : xxx,
"2号男嘉宾" : xxx,
"3号男嘉宾" : xxx,
}
OK,JSON的大致结构出来了,再深入一层,看看xxx是什么。很明显,是一个Java的User对象,还是对应{}:
{
"1号男嘉宾" : {},
"2号男嘉宾" : {},
"3号男嘉宾" : {},
}
那么,这个User对象有哪些字段呢?填上即可:
{"1号男嘉宾": {"name": "雅木茶","age": 23},"2号男嘉宾": {"name": "卡卡罗特","age": 23},"3号男嘉宾": {"name": "贝吉塔","age": 22}
}
其他两个分析过程同理,就不演示了(思考一下,如果User里面有Department会是什么样)。
刚才是顺着来,现在我们玩一下“逆推”。假设现在前端跑过来告诉你
大佬,这个接口我到时候这样传参给你行吗?
[{"name": "张飞","age": 18,"tags": ["大眼睛","大胡子"]},{"name": "关羽","age": 19,"tags": ["万人敌","长胡子"]},{"name": "刘备","age": 20,"tags": ["刘皇叔","摔阿斗"]}
]
此时你应该如何设计入参才能接受前端这种格式的JSON呢?
jackson的一些操作
之前介绍过,服务器本身没有能力处理JSON和文件上传,都要靠第三方组件。SpringBoot则引入了jackson作为默认的JSON组件,其他常见的还有阿里的fastJson和谷歌的gson。
这里介绍一下jackson常见的几个注解。
@Slf4j
@RestController
public class UserController {@PostMapping("/addUser")public UserPOJO addUser(@RequestBody UserPOJO user) {user.setAge(null);return user;}}@Data
public class UserPOJO {/*** 姓名*/private String name;/*** 年龄*/private Integer age;/*** 用户类型*/private Integer userType;/*** 个人标签*/private List<String> tags;}

@JsonInclude
有时我们希望如果字段为null就不要返回给前端,可以使用@JsonInclude,它可以指定很多属性。



@JsonInclude还可以加在类上,那么该对象所有为null的字段都不会参与JSON序列化。
@JsonProperty
对于一些老项目或者其他什么原因,原本传参使用的是下划线,比如user_type,而后端用Java改写时又要符合驼峰命名,此时可以用@JsonProperty做一层“隔离”。

此时出入参都必须叫user_type:

@JsonFormat
有些同学容易把@JsonFormat和@DateTimeFormat搞混,我们单独开一个小节聊一聊。
时间格式
首先和大家说一下,数据库字段无论是datetime还是timestamp,其实都是可以自动对应Java的Date对象,一般讨论的所谓时间格式,都是指前端的显示格式:

刚才我们讨论为什么需要JSON时,提到一个观点:对象是在内存中存活的,无法直接进行网络传输。但是大家有没有想过:
class User {private String name;private Date birthday;
}
其实里面的字段也是对象,也要进行序列化。SpringBoot引入了jackson作为JSON序列化的组件,其中必然包括对Date进行序列化/反序列化的方案。
然而,SpringBoot1.x和2.x其实有较大的改动,其中就包括对Date格式化的改动。大家可以沿用刚才的项目,给UserPOJO加上birthday字段,然后在SpringBoot1.5.9和SpringBoot2.3.4环境下实验。
@Slf4j
@RestController
public class UserController {@PostMapping("/addUser")public UserPOJO addUser(@RequestBody UserPOJO user) {return user;}}
SpringBoot1.x


SpringBoot2.x


有两个细节:
- SpringBoot1.x的返回值是毫秒数,SpringBoot2.x是另一种格式
- 当入参和出参时间格式不同时,会发生转换,此时会出现时间差
-
- SpringBoot1.x传递2020-12-07T22:58:11.000+00:00,返回1607381891000(+8)
- SpringBoot2.x传递1607353091000,返回2020-12-07T14:58:11.000+00:00(-8)
可以通过时间戳转换验证一下(注意单位)。
时差的问题可以通过配置解决,比如:
spring.jackson.time-zone=GMT+8
总的来说就是:

如果希望更改出入参的时间格式,可以有局部和全局两种方式:
- 局部:@JsonFormat / @DateTimeFormat
- 全局
-
- YAML
- Config
@DateTimeFormat只适用于非JSON的POST请求,也就是说,如果项目本身都是JSON请求,你用@DateTimeFormat是无效的。你可以简单理解为:
- @DateTimeFormat,走表单请求时间转换器(适用于GET、POST表单请求)
- @JsonFormat,走JSON请求时间转换器(适用于POST JSON请求、JSON响应)
所以,你在这煞费苦心地调整表单请求的转换格式有啥用?
这里演示一下@JsonFormat的用法:
/*** 生日(时间格式很容易写错,可以抽取为常量或者使用第三方提供的,比如hutool就有)*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;

此时出入参都变为指定格式:

也可以在YAML中进行全局配置:

上面那个mvc:date-format是对表单请求的配置。
或者使用Config:
@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper getObjectMapper() {ObjectMapper objectMapper = new ObjectMapper();// 全局配置objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));return objectMapper;}}
如果说@JsonInclude/@JsonFormat加到字段上、类上分别是字段级别、类级别,那么上面的配置就是整个项目级别,因为Spring的bean默认单例,而这个唯一的ObjectMapper已经被做了手脚,最终所有接口的序列化/反序列化行为都被改写。
这种全局和局部的思想,后面会很常见,这里先点一下。
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
进群,大家一起学习,一起进步,一起对抗互联网寒冬
相关文章:
前后端数据传输格式(下)
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 上篇主要复习了HTTP以及…...
mysql pxc高可用离线部署(三)
pxc学习流程 mysql pxc高可用 单主机 多主机部署(一) mysql pxc 高可用多主机离线部署(二) mysql pxc高可用离线部署(三) mysql pxc高可用 跨主机部署pxc 本文使用docker进行安装,主机间通过…...
XXL-JOB 日志表和日志文件自动清理
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🌺 仓库主页: Gitee 💫 Github 💫 GitCode 💖 欢迎点赞…...
常用sql记录
备份一张表 PostgreSQL CREATE TABLE new_table AS SELECT * FROM old_table;-- 下面这个比上面好,这个复制表结构时,会把默认值、约束、注释都复制 CREATE TABLE new_table (LIKE old_table INCLUDING ALL) WITHOUT OIDS; INSERT INTO new_table SELE…...
设备温度和振动综合监测:温振一体式传感器的优点和应用
随着工业设备的复杂性和自动化程度的提高,对设备状态监测的需求也日益增加。温振一体式传感器作为一种集振动和温度监测于一体的传感器,具备多项优势,因此在工业设备状态监测领域得到广泛应用。 温振一体式传感器基于振动传感器和温度传感器的…...
彻底解决ModuleNotFoundError: No module named ‘exceptions‘【Bug完美解决】
文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结心得项目场景: 根据本文可找到bug原因并彻底解决**ModuleNotFoundError: No module named ‘exceptions‘**Bug 报错: E:\Anconda\python.exe c:\Users\24190\PycharmProjects\pythonProject4py尝试 gong…...
yarn和npm的区别
2023-12-8 yarn和npm的区别 是常用的包管理工具,用于node.js项目中安装、管理、和更新依赖项 有以下几个区别: 性能和速度:在包的安装和下载方面,yarn比npm更快速,yarn通过并行下载和缓存等优化策略,可以…...
设计图中时序图
设计图中的时序图通常用于展示两个或多个对象之间的交互和消息传递的顺序。它是一种用于描述软件或系统中的并发性和时序行为的工具。 以下是一个简单的时序图的示例: 首先,在时序图中创建两个对象,例如"对象A"和"对象B&quo…...
反射实现tomcat
获取类信息的方法 1.通过类对象 x.getClass() 2.通过class.forname方法 Class.forname(className);这里className是存储类名的字符串 3.通过类名.class 类名.class 通过类名创建对象 类名.newInstance(); 反射可以看到类的一切信息࿱…...
Ubuntu 安装 CUDA 和 cuDNN 详细步骤
我的Linux系统背景: 系统和驱动都已安装。 系统是centos 8。查看自己操作系统的版本信息:cat /etc/issue或者是 cat /etc/lsb-release 用nvidia-smi可以看到显卡驱动和可支持的最高cuda版本,我的是12.2。驱动版本是535.129.03 首先&#…...
ArkTS快速入门
一、概述 ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript(简称TS)基本语法风格的基础上,对TS的动态类型特性施加更严格的约束,引入静态类型。同时,提供了声明式UI、状态管理等相应的能力,让开发者可以…...
HTTP不同场景下的通信过程和用户上网认证过程分析
目录 HTTP不同场景的通信过程 HTTP正常交互过程 HTTP透明加速传输过程 HTTP代理服务器场景下交互过程 通过AC对上网用户不同场景的认证过程 AC上网认证正常交互过程 通过Cookie实现免认证交互过程 代理服务器场景下HTTP密码认证交互过程 HTTP不同场景的通信过程 HTTP、…...
VR 实现 Splash Screen 效果
文章目录 背景官方实现逆向分析 背景 手机 App 在实现 Splash Screen 的时候,目前都有成熟的方案可以参考,但是在做 VR 开发时,要如何实现一个 App 自己的 Splash Screen ,下面是我们基于 PICO & OCULUS 进行业务开发时经过探…...
HarmonyOS学习--TypeScript语言学习(一)
注意:这只是我学习的笔记!!! 注意:这只是我学习的笔记!!! 注意:这只是我学习的笔记!!! 本章目录如下: 一、TypeScript语言…...
【C语言】函数递归详解(一)
目录 1.什么是递归: 1.1递归的思想: 1.2递归的限制条件: 2.递归举例: 2.1举例1:求n的阶乘: 2.1.1 分析和代码实现: 2.1.2图示递归过程: 2.2举例2:顺序打印一个整数的…...
WT588F02B-8S语音芯片助力破壁机:智能声音播放提示IC引领健康生活新潮流
在追求健康饮食的时代潮流中,破壁机作为榨汁、搅拌的重要厨房电器,融入智能技术的趋势不断加强。唯创知音的WT588F02B-8S语音芯片作为声音播放提示IC,为破壁机注入了更智能、便捷的声音提示功能,引领用户迈入健康生活的新潮流。 …...
NXP iMX8M Plus Qt5 双屏显示
By Toradex胡珊逢 简介 双屏显示在显示设备中有着广泛的应用,可以面向不同群体展示特定内容。文章接下来将使用 Verdin iMX8M Plus 的 Arm 计算机模块演示如何方便地在 Toradex 的 Linux BSP 上实现在两个屏幕上显示独立的 Qt 应用。 硬件介绍 Verdin iMX8M Plu…...
RepidJson中Writer类、FilewriteStream类、 PrettyWriter类的区别
rapidjson是一个C的JSON解析库,可以用于解析和序列化JSON数据。 Writer是rapidjson中一种基本的输出流,用于将JSON数据输出到字符串或文件中。 FileWriteStream是一个Writer的子类,它专门用于将JSON数据输出到文件中。相比于普通的Writer&a…...
IntelliJ idea卡顿解决,我遇到的比较管用的方案
Setttings> Build, Execution,Deployment>Debugger> Data Views> Java 取消 Enable "toString()" object view; Speed up debugging in IntelliJ Yesterday, I observed painfully slow debugging in IntelliJ. Every step over or step in took almost…...
Fabric.js 实战开发使用介绍
原生canvas用的多的有哪些槽点就不用我多说了;fabric 作为一个canvas库,提供了非常高效、直观的API操作,使我们对涉及canvas相关的功能开发效率大幅提升~~~~ 简单记录下自己的心得;以下是对比canvas来说的优势: 1.简…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
表单设计器拖拽对象时添加属性
背景:因为项目需要。自写设计器。遇到的坑在此记录 使用的拖拽组件时vuedraggable。下面放上局部示例截图。 坑1。draggable标签在拖拽时可以获取到被拖拽的对象属性定义 要使用 :clone, 而不是clone。我想应该是因为draggable标签比较特。另外在使用**:clone时要将…...
【题解-洛谷】P10480 可达性统计
题目:P10480 可达性统计 题目描述 给定一张 N N N 个点 M M M 条边的有向无环图,分别统计从每个点出发能够到达的点的数量。 输入格式 第一行两个整数 N , M N,M N,M,接下来 M M M 行每行两个整数 x , y x,y x,y,表示从 …...
【Java】Ajax 技术详解
文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...
