好长时间没写 Java,发现序列化、反序列化一个 JSON 数据真不是个容易的事情(主要还是年纪大了,记不住)。于是记录一下使用 Gson 反序列化的方法。文中涉及的代码都可以在这个 gson-deserialization-example 中找到。
作者本身不懂 Java,本着不负责的态度写下这些内容,大牛勿喷,想抄代码的菜鸟请珍重。
本文基于转换期间没有异常情况讨论,实际情况请珍重。
初始化
我们先创建一个简单的 TestMain.java 文件,用来运行我们后续的测试方法。
TestMain.java1 2 3 4 5
| public class TestMain { public static void main(String args[]){ } }
|
通过 javac TestMain.java
会生成 TestMain.class 文件,之后就能通过 java TestMain
运行 class 文件。
在当前的例子中我们 google 的 Gson( maven 地址) 库来做序列化和反序列化。简单起见,我们直接下载 jar 包,放到 lib 目录下。
因为我们使用了 gson.jar 所以命令会变成这样:
javac -classpath lib/gson-2.8.0.jar TestMain.java
java -classpath .:lib/gson-2.8.0.jar TestMain
JsonParser
Object
假设我们有这样一个简单的 JSON 数据:
1 2 3 4 5 6 7 8 9 10 11 12
| /* Sample JSON */ { "name": "Hans Chan", "age": 18, "tags": [{ "id": 1, "text": "JavaScript" }, { "id": 2, "text": "Java" }] }
|
使用 JsonParser
足够的简单:
1 2
| JsonParser jsonParser = new JsonParser(); JsonElement userJsonElement = jsonParser.parse(json);
|
所有东西都是 抽象 的 JsonElement
(api),如果要获取具体的内容,就得转换成 JsonObject
或 JsonArray
等类型,获取方式也非常直观 .get("key")
:
1 2 3 4
| JsonObject userJsonObject = userJsonElement.getAsJsonObject(); String name = userJsonObject.get("name").getAsString(); int age = userJsonObject.get("age").getAsInt(); JsonArray userTagsJsonArray = userJsonObject.get("tags").getAsJsonArray();
|
Array
同样的场景,如果输入的 json
字符串不是 {}
而是 []
,也可以通过上述方法获取:
1 2 3 4 5 6
| String json = "[{},{}]"; JsonArray userJsonArray = jsonParser.parse(json).getAsJsonArray(); for (int i = 0; i < userJsonArray.size(); i++) { JsonObject userJsonObject = userJsonArray.get(i).getAsJsonObject(); }
|
Serialization
JsonElement
的序列化很简单,直接 .toString()
即可。
1
| String json = userJsonObject.toString();
|
OO
这下子,写 Java 的哥们就肯定会跳出来说 “这是什么鬼,一点都不 OO”。的确上面的方式很 js,于是我们就要写得像 Java 一点,先来两个 class :
1 2 3 4 5 6 7 8 9 10 11
| private class Tag { private int id; private String text; } private class User { private String name; private int age; private List<Tag> tags; }
|
Object
大家注意了,我要变形了!(敲黑板)
1 2
| Gson gson = new Gson(); User user = gson.fromJson(json, User.class);
|
通过 Gson,String
被转换成指定的 User.class
,然后我们就可以愉快地操作这个实例了:
1 2 3 4 5
| List<Tag> tags = user.getTags(); for (int i = 0; i < tags.size(); i++) { Tag tag = tags.get(i); System.out.println("tag " + tag.getId() + ": " + tag.getText()); }
|
Array
还是同样的例子,如果是 []
怎么办?我们当然期望是获得一个 List<User>
啦,但没有 List<User>.class
这个东西,怎么破?没关系,Gson 里面还有个 TypeToken
是可以跟你干这事的,我们只需要这样:
1 2 3 4 5 6
| TypeToken typeToken = new TypeToken<List<User>>() {};
Type type = typeToken.getType();
List<User> users = gson.fromJson(json, type);
|
还是可以愉快地玩耍的,不是麽 😂
Serialization
Class 要反序列化就还是要依赖回 Gson 提供的 toJson
方法:
1
| String json = gson.toJson(user);
|
GsonBuilder
很多时候,输入的 json 总有那么一点不尽人意,例如下面这个例子:
1 2 3 4 5 6 7 8 9 10 11 12
| { "id": 3, "name": "Hans Chan", "registrationTime": "1999-09-19 18:10:22", "data": { "some": "complex data", "we": { "do-NOT": ["care", "about", "what", "inside"], "BUT": "needed!" } } }
|
id
是我们需要的数据,但序列化出去的时候不想显示
registrationTime
可能不是一个我们想要的格式
data
可能是我们不是很关心结构,但又需要保存里面的内容
利用 GsonBuilder 和 Annotation 我们就可以实现上面两个功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| private class BaseUser { @Expose(serialize = false, deserialize = true) private int id;
@Expose private String name;
@SerializedName("registrationTime") @Expose private Date registration;
public int getId() { return id; } public Date getRegistration() { return registration; } } private class CustomBUser extends BaseUser { @Expose private JsonElement data;
public JsonElement getData() { return data; } }
|
registrationTime
的格式我们用 GsonBuilder
声明:
1 2 3 4 5 6
| Gson deserializationGson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .setDateFormat("yyyy-MM-dd HH:mm:ss") .create();
|
愉快地玩耍吧:
1 2 3
| CustomBUser cbu = deserializationGson.fromJson(json, CustomBUser.class); System.out.println("id: " + cbu.getId()); System.out.println(cau.getData());
|
自定义序列化和反序列化
上面的例子中,data
是直接用一个 JsonElement
来处理的,如果有更加个性化的要求,那就需要自己写序列化和反序列化方法了。这里我们自己实现一个 CustomUserData
类,用来处理 data
数据,直接实现上面相同的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| private static class CustomUserData { private JsonElement ctx; public CustomUserData(JsonElement ctx) { this.ctx = ctx; } public String toString() { return this.ctx.toString(); } } private class CustomAUser extends BaseUser { @Expose private CustomUserData data;
public CustomUserData getData() { return data; } }
private static class CustomUserDataDeserializeAdapter implements JsonDeserializer<CustomUserData> { @Override public CustomUserData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return new CustomUserData(json); } }
public static class CustomUserDataSerializeAdapter implements JsonSerializer<CustomUserData> { @Override public JsonElement serialize(CustomUserData src, Type typeOfSrc, JsonSerializationContext context) { return src.ctx; } }
|
通过 registerTypeAdapter
给 GsonBuilder 注册上面的自定义序列化方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Gson deserializationGson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .setDateFormat("yyyy-MM-dd HH:mm:ss") .registerTypeAdapter(CustomUserData.class, new CustomUserDataDeserializeAdapter()) .create(); Gson serializationGson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .setDateFormat("yyyy/MM/dd HH:mm:ss") .registerTypeAdapter(CustomUserData.class, new CustomUserDataSerializeAdapter()) .setPrettyPrinting() .create();
System.out.println("---------- CustomAUser ----------"); CustomAUser cau = deserializationGson.fromJson(json, CustomAUser.class); System.out.println("id: " + cau.getId());
System.out.println(serializationGson.toJson(cau));
|