当前位置: 首页 > news >正文

数据格式 、序列化和反序列化

目录

网络通信的完整层次:

各层的数据形态变化

1. 应用层 - 我们看到的数据格式

2. 序列化后 - 准备发送的数据

3. 传输层 - 封装为数据段

4. 网络层 - 封装为数据包

5. 数据链路层 - 封装为帧

6. 物理层 - 真正的二进制比特流

为什么我们还要关心数据格式?

区别

1. 序列化/反序列化开销

2. 数据体积对比

3. 解析性能对比

为什么格式选择仍然重要?

1. 开发效率

2. 调试便利性

3. 跨平台兼容性

序列化和反序列化

序列化(Serialization)

反序列化(Deserialization)

Java对象的序列化和反序列化(JSON)

 Jackson(Spring Boot 默认)

手动进行序列化与反序列化

 WebSocket 中的 JSON 消息处理

自定义序列化与反序列化


网络通信的完整层次:

应用层:JSON/XML/Protobuf 等格式↓
表示层:数据序列化/反序列化  ↓
会话层:建立/维护连接↓
传输层:TCP/UDP 数据段↓
网络层:IP 数据包↓
数据链路层:以太网帧↓
物理层:电信号/光信号/无线电波(二进制比特流)

各层的数据形态变化

1. 应用层 - 我们看到的数据格式

// 这是我们在代码中处理的数据
{"name": "张三","age": 25,"email": "zhangsan@example.com"
}

2. 序列化后 - 准备发送的数据

// JSON 序列化为字节
byte[] jsonBytes = "{\"name\":\"张三\",\"age\":25,\"email\":\"zhangsan@example.com\"}".getBytes("UTF-8");// 或者 Protobuf 序列化
byte[] protobufBytes = user.toByteArray();

3. 传输层 - 封装为数据段

[TCP Header][应用层数据字节]
源端口:8080 目标端口:80 序列号:123 确认号:456 ... + jsonBytes/protobufBytes

4. 网络层 - 封装为数据包

[IP Header][TCP数据段]
源IP:192.168.1.100 目标IP:93.184.216.34 ... + [TCP数据段]

5. 数据链路层 - 封装为帧

[以太网头][IP数据包][CRC校验]
MAC地址:00:1A:2B:3C:4D:5E 目标MAC:00:1A:2B:3C:4D:5F ... + [IP数据包]

6. 物理层 - 真正的二进制比特流

01101000 01110100 01110100 01110000 00111010 00101111 00101111  ...
h        t        t        p        :        /        /

为什么我们还要关心数据格式?

虽然底层都是二进制,但不同格式在应用层有重大区别:

不同的应用层数据格式

格式类型人类可读体积性能复杂度主要用途
JSON文本中等中等Web API、配置
XML文本企业系统、文档
YAML文本配置文件
CSV文本很小很低数据表格
Protocol Buffers二进制×很小很高微服务、gRPC
MessagePack二进制×网络传输
Avro二进制×很小很高大数据
BSON二进制×中等MongoDB

区别

1. 序列化/反序列化开销
// JSON 序列化(文本,相对较慢)
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(user);  // 需要处理字符串编码
byte[] bytes = json.getBytes("UTF-8");// Protobuf 序列化(二进制,很快)
byte[] bytes = user.toByteArray();  // 直接生成优化过的二进制
2. 数据体积对比

假设要传输这个用户数据:

User user = new User("张三", 25, "zhangsan@example.com");

不同格式的体积:

  • JSON{"n":"张三","a":25,"e":"zhangsan@example.com"} → 约 60 字节

  • XML<user><n>张三</n><a>25</a><e>zhangsan@example.com</e></user> → 约 80 字节

  • Protobuf: 优化的二进制格式 → 约 20-30 字节

3. 解析性能对比
// JSON 解析需要词法分析、语法分析
JSON.parse('{"name":"张三","age":25}');// Protobuf 解析直接按照预定义格式读取二进制
user.parseFrom(byteArray);

为什么格式选择仍然重要?

1. 开发效率
// JSON:前端直接使用
const user = JSON.parse(response);
console.log(user.name); // 直接访问// Protobuf:前端需要额外处理
const user = UserProto.decode(new Uint8Array(response));
console.log(user.getName()); // 需要通过getter
2. 调试便利性
// JSON:人类可读,调试方便
System.out.println("收到数据: " + new String(bytes, "UTF-8"));
// 输出: 收到数据: {"name":"张三","age":25}// Protobuf:二进制,调试困难
System.out.println("收到数据: " + Arrays.toString(bytes));
// 输出: 收到数据: [10, 6, -28, -67, -96, -27, -91, -67, 16, 25, 26, 21, 122, 104, 97, 110, 103, 115, 97, 110, 64, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109]
3. 跨平台兼容性
# Python 解析 JSON(内置支持)
import json
user = json.loads('{"name": "张三", "age": 25}')# Python 解析 Protobuf(需要额外安装库)
import user_pb2
user = user_pb2.User()
user.ParseFromString(binary_data)

序列化和反序列化

序列化(Serialization)

序列化是指将数据结构或对象状态转换为可以存储或传输的格式的过程。在序列化过程中,对象的状态(包括其数据和其他必要信息)被转换成一种标准格式,比如字节流。这个字节流可以存储在文件中,也可以通过网络传输到另一个系统。

序列化的目的:

  • 持久化:将对象的状态保存到存储介质(如硬盘)中,以便以后重新创建该对象。

  • 网络传输:将对象转换为可以通过网络发送的格式,以便在不同的系统或进程之间交换数据。

例如,在Java中,你可以将一个User对象序列化成JSON字符串,然后把这个字符串保存到文件或者通过网络发送。

反序列化(Deserialization)

反序列化是序列化的逆过程,即将序列化后的数据(如字节流)重新转换回数据结构或对象的过程。这个过程会根据序列化时的规则和格式,将数据还原成原来的对象。

反序列化的目的:

  • 从存储介质中读取数据并重新构建对象。

  • 从网络接收数据并还原成对象。

例如,从网络接收到一个JSON字符串,然后将其反序列化成Java的User对象。

应用层的数据格式”指的是序列化后所采用的数据表示形式。

网络传输的永远是序列化后的数据,而这些数据是按照某种数据格式规范生成的。

选择数据格式的核心原则:

  1. 不要过早优化:先使用JSON验证业务逻辑

  2. 基于数据决策:用性能测试结果指导选择

  3. 考虑总成本:包括开发、维护、运维成本

  4. 保持灵活性:可以在不同场景使用不同格式

简单决策指南:

  • Web开发:JSON(默认选择)

  • 微服务:Protocol Buffers + gRPC

  • 配置文件:YAML

  • 大数据:Avro/Parquet

  • 实时系统:MessagePack/Protobuf

  • 移动端:Protobuf/FlatBuffers

Java对象的序列化和反序列化(JSON)

在Spring Boot框架下,Java对象的序列化和反序列化(特别是与JSON相关的)有很多工具选择。以下是一些常用的工具和库:

  1. Jackson:Spring Boot默认使用的JSON处理库,功能强大,支持流式处理、树模型、数据绑定等。

  2. Gson:Google提供的JSON库,简单易用。

  3. JSON-B:Java EE的JSON绑定标准,其参考实现是Yasson。

  4. Fastjson:阿里巴巴提供的JSON库,性能较好,但曾经出现过一些安全漏洞。

在Spring Boot中,默认使用Jackson。因此,如果你没有特殊配置,Spring Boot会自动配置Jackson来进行序列化和反序列化。

工具类型Spring Boot 默认性能易用性特性
JacksonJSON 处理功能丰富,生态完善
GsonJSON 处理×很高API 简单
JSON-BJSON 绑定标准×Java EE 标准
FastjsonJSON 处理×很高阿里出品,性能强

 Jackson(Spring Boot 默认)

依赖配置

<!-- Spring Boot 已经自动包含,无需手动添加 -->
<!-- 但如果需要明确声明: -->
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId>
</dependency><!-- 额外功能模块 -->
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

 Jackson 基础配置

在 Spring Boot 中,Jackson 的自动化配置主要通过 application.properties 或 application.yml 文件完成。

常用配置项示例(application.properties):

# 设置日期格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
# 设置时区
spring.jackson.time-zone=GMT+8
# 序列化时,忽略值为 null 的属性
spring.jackson.default-property-inclusion=non_null
# 忽略未知属性(反序列化时),防止解析失败
spring.jackson.deserialization.fail-on-unknown-properties=false

如果需要更精细的控制,可以创建一个自定义的 ObjectMapper Bean。

@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper objectMapper() {ObjectMapper objectMapper = new ObjectMapper();// 禁用未知属性导致反序列化失败objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);// 序列化时忽略空BeanobjectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);// 设置日期格式objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));// 其他自定义配置...return objectMapper;}
}

HTTP 接口中的自动化序列化

在 Spring Boot 的 Controller 中,Jackson 的使用几乎是透明的。

1. 对象序列化为 JSON 响应:
使用 @RestController 注解的控制器,其方法返回值会自动被序列化为 JSON

@RestController
@RequestMapping("/api/users")
public class UserController {@GetMapping("/{id}")public User getUser(@PathVariable Long id) {// Spring Boot 会自动将 User 对象序列化为 JSON 返回return userService.findById(id);}
}

2. 反序列化 JSON 请求体:
使用 @RequestBody 注解将请求体中的 JSON 数据转换为 Java 对象。

@PostMapping
public User createUser(@RequestBody User user) {// user 对象是从请求的 JSON 数据反序列化而来return userService.save(user);
}

3. 字段重命名与忽略:
使用 Jackson 注解控制序列化细节。

public class User {@JsonProperty("user_id") // 序列化后字段名为 "user_id"private Long id;private String username;@JsonIgnore // 忽略该字段,不参与序列化和反序列化private String password;// getters and setters
}

手动进行序列化与反序列化

在一些非 HTTP 接口的场景(如工具类、日志记录、消息队列等),你需要手动使用 ObjectMapper

@Service
public class MyService {@Autowiredprivate ObjectMapper objectMapper; // 注入 Spring Boot 配置好的 ObjectMapper/*** 将对象序列化为 JSON 字符串*/public String serializeObject(Object obj) throws JsonProcessingException {return objectMapper.writeValueAsString(obj);}/*** 将 JSON 字符串反序列化为对象*/public <T> T deserializeString(String json, Class<T> clazz) throws JsonProcessingException {return objectMapper.readValue(json, clazz);}
}

美化输出:
使用 writerWithDefaultPrettyPrinter() 方法可以获得格式化的、易于阅读的 JSON 字符串

String prettyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(prettyJson);

 WebSocket 中的 JSON 消息处理

在使用 Spring 的 WebSocket 特别是 STOMP 协议时,默认的消息转换器可能不支持 JSON,需要显式配置 Jackson。

1. 引入依赖:
确保项目中包含了 jackson-databind 依赖

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

2. 配置 WebSocket 消息转换器:
在 WebSocket 配置中,注册一个 MappingJackson2MessageConverter

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.enableSimpleBroker("/topic");registry.setApplicationDestinationPrefixes("/app");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/ws").withSockJS();}@Overridepublic boolean configureMessageConverters(List<MessageConverter> messageConverters) {// 添加 Jackson 消息转换器,用于处理 JSON 格式的 WebSocket 消息messageConverters.add(new MappingJackson2MessageConverter());return false; // 设置为 false 表示不注册默认的转换器}
}

3. 在控制器中收发消息:
配置好后,在 WebSocket 控制器中就可以直接使用 Java 对象接收和发送 JSON 消息。

@Controller
public class WebSocketController {@MessageMapping("/chat")@SendTo("/topic/messages")public ChatMessage sendMessage(ChatMessage chatMessage) {// 直接处理 ChatMessage 对象,Jackson 会自动完成反序列化和序列化return chatMessage;}
}

自定义序列化与反序列化

对于特殊数据类型(如 LocalDateTime)或复杂转换逻辑,Jackson 提供了强大的自定义功能。

场景:处理 LocalDateTime
默认情况下,Jackson 无法直接序列化 LocalDateTime,需要自定义转换器-10。

方法一:使用 @JsonSerialize 和 @JsonDeserialize 注解

  1. 自定义序列化器:

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");@Overridepublic void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {gen.writeString(value.format(formatter));}
}

  2. 自定义反序列化器:

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");@Overridepublic LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {return LocalDateTime.parse(p.getValueAsString(), formatter);}
}

  3.在实体类字段上应用:

public class Event {private String name;@JsonSerialize(using = LocalDateTimeSerializer.class)@JsonDeserialize(using = LocalDateTimeDeserializer.class)private LocalDateTime eventTime;// getters and setters
}

方法二:通过 Module 注册(推荐,全局生效)

  1. 创建自定义 Module:

@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper objectMapper() {ObjectMapper objectMapper = new ObjectMapper();// 注册 Java 8 时间模块,这是处理 Java 8 日期时间更标准、简便的方式objectMapper.registerModule(new JavaTimeModule());// 禁用将日期序列化为时间戳objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);return objectMapper;}
}

http://www.dtcms.com/a/532557.html

相关文章:

  • 【Rust编程:从新手到大师】第一个项目Hello,World
  • 【代码随想录算法训练营——Day48】单调栈——42.接雨水、84.柱状图中最大的矩形
  • 架构设计:银行核心系统
  • 手机网站建设推广网站策划书格式
  • Week 22: 深度学习补遗:Transformer+Encoder构建
  • Nacos,什么是Nacos,一文详解
  • 7. Qt 容器迭代器
  • 详解MySQL两种存储引擎MyISAM和InnoDB的优缺点
  • Python + uiautomator2 手机自动化控制教程
  • 黑龙江省城乡建设厅网站注册广告公司名字
  • 杨校老师课堂之C++备赛信奥中STL常用库函数梳理汇总(含样例代码)
  • UU远程深度测评:聚焦游戏与Windows多屏场景,免费实用的远程控制选择
  • week7
  • 【Python刷力扣hot100】15. 3Sum
  • MacOS平台Keil代替方案
  • 建设项目技术服务网站笋岗网站建设
  • 【AI原生架构:数据架构】10、从主数据治理到价值落地
  • jQuery JSONP详解
  • GitHub等平台形成的开源文化正在重塑和解
  • 网站首页包含的内容wordpress扩展class名称
  • MCoT在医疗AI工程化编程的实践手册(上)
  • 济南网站建设淄博外贸网站哪家好
  • 阮一峰《TypeScript 教程》学习笔记——类型工具
  • 怎样做钓鱼网站网站建设电话营销话术
  • 51c大模型~合集32
  • 生物化学Learning Track(14)酶催化机制
  • 力扣2:两数相加
  • 构建通用并发下载工具:用Golang重构wget脚本的实践分享
  • 多国语言 网站源码邦邻营销型网站建设
  • 深圳网站制作服杭州专业网站