Java中的JSON解析

Json 是一种是一种轻量级的文本数据交换格式。便于数据传输、存储、交换;目前使用非常广泛。
Java 中常见的解析库有:JSON.simple、 GSON、Jackson、JSONP、FastJson 等。
本文主要介绍 Gson 和 Jackson 的使用,以及 Json 格式的科普。

关于json数据格式

其主要由三部分组成:

  • 名称/值
    无序、一个对象用“{}”包括,名称和值间用“:”相隔,对象间用“,”隔开;
    "name":"html"

  • 对象
    一个JSON对象包括多个名称/值对,在花括号里书写
    { "name":"html","year":"5"}

  • 数组
    数组以“[]”包括,数据的对象用逗号隔开

    1
    2
    3
    4
    5
    6
    7
    8
    [{
    "name":"html",
    "year":"5"
    },
    {
    "name":"ht",
    "year":"4"
    }]

    数组包含对象,对象包含值/对

使用GSON解析

GSON 这个Java库能够在Java对象和JSON间进行相互转换。同时它还提供了对Java泛型的完整支持,而且还不需要你在类上面添加注解。无需添加注解使用起来则更为便捷,同时在无法修改源代码的情况下这还是一个必要的先决条件。目前在 Android 开发中是比较流行的一种解析方式。强烈建议看这篇原文点我起飞
它基于事件驱动,根据所需要取的数据(不用将全部的数据进行 JavaBean 的编写)通过建立一个对应于 JSON 数据的 JavaBean 类就可以通过简单的操作解析出所需 JSON 数据
步骤:
创建一个与 JSON 数据对应的 JavaBean 类(用作存储需要解析的数据)
GSON 解析的关键是重点是要根据 json 数据里面的结构写出一个对应的 javaBean,规则是:

  • JSON 的大括号对应一个对象,对象里面有 key 和 value (值)。在 JavaBean 里面的类属性要和key同名。
  • JSON 的方括号对应一个数组,所以在 JavaBean 里面对应的也是数组,数据里面可以有值或者对象。
    如果是一个对象数组,那就要用list<obj>来装了
  • 如果数组里面只有值没有 key,就说明它只是一个纯数组,如果里面有值有 key,则说明是对象数组。纯数组对应 JavaBean 里面的数组类型,对象数组要在 Bean 里面建立一个内部类,类属性就是对应的对象里面的 key,建立了之后要创建一个这个内部类的对象,名字对应数组名。
  • 对象里面嵌套对象时候,也要建立一个内部类,和对象数组一样,这个内部类对象的名字就是父对象的key

注:JavaBean 类里的属性不一定要全部和 JSON 数据里的所有 key 相同,可以按需取数据,也就是你想要哪种数据,就把对应的 key 属性写出来,注意名字一定要对应
关键在于根据 json 数据构建出一个 Javabean 文件,解析非常简单,一句代码

JavaBean对象 = gson.fromJson(json字符串,javaBean类类名.class);

1
2
3
4
5
6
7
8
9
10
Gson gson = new Gson();
//创建实体
Student student = new EntityStudent();

//用GSON方法将JSON数据转为单个类实体 json变量为json格式的字符串
student = gson.fromJson(json,Student.class);

//将Java集合转换为json
//list变量可以是JavaBean也可以是Collection集合
String json2 = gson.toJson(List);

有时候我们解析的可能是一段JSON数组,这个可能麻烦点,我们用List来装

1
List<Student> stuList = gson.fromJson(json, new TypeToken<List<Student>>(){}.getType());

也是一行代码,相比手动实现还是简单的
总之:
凡是看到 { 就是一个JsonObject
凡是看到 [ 就是一个JsonArray

json字符串转Java类

使用Jackson解析

Jackson 是一个数据处理的工具套件,它的亮点是流式的JSON解析器及生成器。它是专为Java设计的,同时也能处理其它非JSON的编码。从我们在 Github 中的统计来看,它应该是最流行的 JSON 解析器,在 JavaEE 开发中非常常见。
Jackson 最常用的 API 就是基于”对象绑定” 的 ObjectMapper,看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
ObjectMapper mapper = new ObjectMapper(); 
Person person = new Person();
person.setName("Tom");
person.setAge(40);
// 序列化
String res = mapper.writeValueAsString(person);
// 输出格式化后的字符串(有性能损耗)
String jsonString = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(person);
// 反序列化
Person deserializedPerson = mapper.readValue(jsonString, Person.class);

ObjectMapper 通过 writeValue 系列方法 将 java 对 象序列化 为 json,并 将 json 存 储成不同的格式:String(writeValueAsString),Byte Array(writeValueAsString),Writer, File,OutStream 和 DataOutput。
ObjectMapper 通过 readValue 系列方法从不同的数据源像 String , Byte Array, Reader,File,URL, InputStream 将 json 反序列化为 java 对象。
下面是对于泛型、集合的支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// List
CollectionType javaType = mapper.getTypeFactory()
.constructCollectionType(List.class, Person.class);
List<Person> personList = mapper.readValue(jsonInString, javaType);
// 第二种方式
List<Person> personList = mapper.readValue(jsonInString, new TypeReference<List<Person>>(){});

// Map
//第二参数是 map 的 key 的类型,第三参数是 map 的 value 的类型
MapType javaType = mapper.getTypeFactory()
.constructMapType(HashMap.class, String.class, Person.class);
Map<String, Person> personMap = mapper.readValue(jsonInString, javaType);
// 第二种方式
Map<String, Person> personMap = mapper.readValue(jsonInString, new TypeReference<Map<String, Person>>() {});

开发中上面的这些基本用法一般就满足需求了,更高级的用法参考:
https://www.ibm.com/developerworks/cn/java/jackson-advanced-application/index.html

常用注解

在定义的 Bean 中可以使用下面的注解来做相应的调整。

注解用法
@JsonProperty用于属性,把属性的名称序列化时转换为另外一个名称。
示例: @JsonProperty("birth_ d ate") private Date birthDate;
@JsonFormat用于属性或者方法,把属性的格式序列化时转换成指定的格式。
示例: @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm") public Date getBirthDate()
@JsonPropertyOrder用于类, 指定属性在序列化时 json 中的顺序
示例: @JsonPropertyOrder({ "birth_Date", "name" }) public class Person
@JsonCreator用于构造方法,和 @JsonProperty 配合使用,适用有参数的构造方法。
示例: @JsonCreator public Person(@JsonProperty("name")String name) {…}
@JsonAnySetter用于属性或者方法,设置未反序列化的属性名和值作为键值存储到 map 中
示例:@JsonAnySetter public void set(String key, Object value) { map.put(key, value); }
@JsonAnyGetter用于方法 ,获取所有未序列化的属性
示例:public Map<String, Object> any() { return map; }

关于JavaBean

其实就是一个Java类,只是他加了 getter 和 setter 方法,将属性暴露在外面

一般具备的特点为:

  1. 所有属性为private
  2. 提供默认空构造方法
  3. 提供 getter 和 setter
  4. 实现 serializable 接口

更多可参考:
Java帝国之JavaBean
wiki
stackoverflow翻译

总结

关于它们的测试参考 【1】【2】,数据是2016、2015 的,仅供参考,这里我就直接说结论:

  • 如果你的应用经常会处理大的 JSON 文件,那么 Jackson 应该是你的菜。GSON在大文件上表现得相当吃力。
  • 如果你主要是处理小文件请求,比如某个微服务或者分布式架构的初始化,那么GSON当是首选。Jackson 在小文件上的表现则不如人意。
  • 如果这两种文件你都经常会处理到,那么在两轮表现中都位居第二的 JSON.simple 对此类场景则更为适合。在不同的文件大小上 Jackson 和 GSON 的表现都不太好。

  • 字符串解析成 JavaBean:当数据量较少时首选 FastJson,数据量较大使用 Jackson。
  • 字符串解析成JSON:当数据量较少时首选FastJson,数据量较大使用Jackson。
  • JavaBean 构造JSON:当数据量较少时选择Gson,数据量较大可使用Jackson。
  • 集合构造JSON:首先Jackson,其次Fastjson。

如果你对 JSON 库的解析速度比较敏感的话,大文件选 Jackson,小文件选 GSON,两者则 JSON.simple;对解析速度要求非常高时可以考虑阿里的 FastJson,但是如果大文件还是选择 Jackson 吧。

我所见到的基本都是 Android 上 Gson 是主力,JavaEE 上 Jackson 是主力。

补充

Android 中json的解析方法一般有两种:

  • 基于事件驱动

  • 基于文档驱动解析方式
    基于文档驱动,类似于 XML 的 DOM 解析方法,先把全部文件读入到内存中,然后遍历所有数据,然后根据需要检索想要的数据。

基于事件驱动的主流方式:Gson 解析和 Jackson 解析
基于文档驱动解析方式的主流方式:Android Studio自带 org.son 解析

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~