背景
前后台交换消息的格式选择JSON时,根据消息的复杂度,JSON数据的复杂度也高低不一。简单的JSON可以直接用官方json库编解码,例如
{
"type": "response", // package类型
"command": "/api/move", // 源指令
"error_message": "", // 错误消息
"status": "OK", // 执行结果
"uuid": "123456" // 用户定义的uuid
}
但复杂的用它就很繁琐了,例如
{
"command": "/api/markers/query_list",
"error_message": "",
"status": "OK",
"type": "response",
"uuid": "",
"results": {
"1": {
"avatar": "默认",
"floor": 11,
"key": 0,
"marker_name": "1",
"pose": {
"orientation": {
"w": -1,
"x": 0,
"y": 0,
"z": 0.02
},
"position": {
"x": -0.72,
"y": -0.35,
"z": 0
}
}
}
}
}
这时官方json库那种一个字段一个字段地处理方式,就太繁琐了。
有没有一种方法,可以定义好数据结构,然后直接通过序列化
(就是将对象编码成json串)、反序列化
(就是从json串里解码出对象)技术自动编解码JSON呢?
解决方案gson
可以用Google出的gson库,它能将Java对象
直接序列化成JSON串
,或者将JSON串
直接反序列化成一个Java对象
,比起官方json库,在处理复杂json数据时的效率高了很多。
创建Util工具类
我们将gson提供的序列化、反序列化方法,都封装到一个工具类里,这样其他地方就能直接拿来用了
package com.lanplus.lanova.chassis_control.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JsonUtils {
private static Gson mGson = new GsonBuilder().serializeNulls().create();
public static String toJson(Object src) {
return mGson.toJson(src);
}
public static <T> T fromJson(String json, Class<T> classOfT) {
return mGson.fromJson(json, classOfT);
}
}
toJson
方法比较简单,将对象传进来,返回的就是编码后的json字符串fromJson
方法比较复杂,第一个入参很简单就是json字符串,第二个参数稍微绕点,表示json串对应的Java对象类型。- gson库应该是利用
反射
技术提取对象类内每个成员的名字
和类型
,然后从json字符串里匹配、提取出字段值。
- gson库应该是利用
对应Java类定义
以复杂json消息为例
package com.lanplus.lanova.chassis_control.entity;
import java.util.HashMap;
public class MarkersResponse {
public String command;
public String error_message;
public String status;
public String type;
public String uuid;
public HashMap<String, Marker> results;
public static class Marker {
public int floor;
public int key;
public String avatar;
public String marker_name;
public Pose pose;
public static class Pose {
public Orientation orientation;
public Position position;
public static class Position {
public double x;
public double y;
public double z;
public Position() {
}
}
public static class Orientation {
public double w;
public double x;
public double y;
public double z;
public Orientation() {
}
}
public Pose() {
}
}
public Marker() {
}
}
public MarkersResponse() {
}
}
说明
- json串里的每个嵌套结构,都对应一个Java内部类,嵌套越深,内部类也越“内部”
- 内部类必须是
public static
修饰的,否则编码时外界无法创建对应类型的Java对象 - 每个字段应该设置成
private
,再提供getter
和setter
接口,但为了简化代码以便说明问题,就搞成public
了。实际项目中可以用lombok库给private字段自动生成getter、setter
编码过程示例
MarkersResponse resp = new MarkersResponse();
MarkersResponse.Marker marker = new MarkersResponse.Marker();
resp.uuid = uuid;
resp.status = "OK";
resp.command = cmd;
resp.results = new HashMap<>();
for (int i = 0; i < 2; i++){
marker.avatar = String.valueOf(i);
marker.floor = curFloor;
marker.key = i;
marker.marker_name = "marker"+i;
marker.pose = new MarkersResponse.Marker.Pose();
marker.pose.orientation = new MarkersResponse.Marker.Pose.Orientation();
marker.pose.orientation.x = i;
marker.pose.orientation.y = i;
marker.pose.orientation.z = i;
marker.pose.orientation.w = i;
marker.pose.position = new MarkersResponse.Marker.Pose.Position();
marker.pose.position.x = i;
marker.pose.position.y = i;
marker.pose.position.z = i;
resp.results.put(marker.marker_name, marker);
}
String msg = JsonUtils.toJson(resp);
解码过程示例
MarkersResponse resp = JsonUtils.fromJson(jsonString, MarkersResponse.class);
// 遍历对象内部字段很简单,除了注意字段可能为null,其他没了
总结
静态类型语言在开发中大型工程时真的比动态类型语言好用太多了