环境准备
首先依旧是相关jar包的准备,此处依旧用maven来管理,配置依赖如下(此处用了apache的common-io包下的FIleUtil来加载文件):
<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
当然,你也可以手动导入jar包,此处提供maven仓库的地址:gson: 点击此处跳转
commons-io: 点击此处跳转
生成JSON数据
此次对比上一篇文章做一个简单Demo来展示目前我接触到的GSON相较于JSON-Java(上一篇文章)的强大之处。
首先依旧是JSON目标数据:
{
"fileName": "test.java",
"encrypted": false,
"author": "devsong",
"function": [
"genJSONFile",
"parseJSONFile"
],
"memberCount": 1,
"description": "a demonstration of how to generate and parse JSON files",
"createDate": "2017-08-05"
}
然后是实体类:JavaFile.java与之前不同的是,该实体类上面我做了一些修改,(1)给fileName属性添加了transient关键字;(2)给function添加了@serialization注解
package org.devsong.bean;
import java.util.Arrays;
import com.google.gson.annotations.SerializedName;
public class JavaFile {
private transient String fileName; //加上了transient关键字,默认不会被转化到JSON数据
private String author;
private String description;
private String createDate;
@SerializedName("FUN") //给成员function添加了注解,在转化的JSON数据中,function属性名将被替换为FUN
private String[] function;
private int memberCount;
private boolean encrypted;
public JavaFile() {
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCreateDate() {
return createDate;
}
public void setCreateDate(String createDate) {
this.createDate = createDate;
}
public String[] getFunction() {
return function;
}
public void setFunction(String[] function) {
this.function = function;
}
public int getMemberCount() {
return memberCount;
}
public void setMemberCount(int memberCount) {
this.memberCount = memberCount;
}
public boolean getEncrypted() {
return encrypted;
}
public void setEncrypted(boolean encrypted) {
this.encrypted = encrypted;
}
@Override
public String toString() {
return "JavaFile [fileName=" + fileName + ", author=" + author + ", description=" + description
+ ", createDate=" + createDate + ", function=" + Arrays.toString(function) + ", memberCount="
+ memberCount + ", encrypted=" + encrypted + "]";
}
}
下面是生成JSON的代码:亮点1: 用GSON处理JSON数据过程中transient的作用即 @SerializedName 注解的作用
/**
* gson: 通过JavaBean构建JSON数据
*/
public static void genJSONByBean() {
JavaFile javaFile = new JavaFile();
javaFile.setFileName("test.java");
javaFile.setAuthor("devsong");
javaFile.setDescription("a demonstration of how to generate and parse JSON files");
javaFile.setCreateDate("2017-08-05");
javaFile.setFunction(new String[] { "genJSONFile", "parseJSONFile" });
javaFile.setMemberCount(1);
javaFile.setEncrypted(false);
System.out.println("============ genJSONByBean =============");
Gson gson = new Gson();
// 由于javaFile的fileName属性用了transient关键字修饰,所以用gson转换为JSON数据时,fileName属性默认不会被转化到JSON
// 所以输出结果中不含fileName一项
System.out.println(gson.toJson(javaFile));
}
乍一看和JSON-java生成步骤一致,的确如此,亮点在于现在JavaFile类中的fileName用了transient修饰,所以在生成的JSON数据中,该属性被忽略,这个关键字在对象序列化与反序列化的时候接触过,此处的作用比较好理解。这样方便我们对转换对象的个性化定制,非常方便,这是GSON相较于JSON-java强大的点之一。另外,由于JavaFile类中的function用了@SerializedName("FUN"),所以在转化为JSON之后,function会被FUN替代。
亮点2:通过JavaBean构建JSON数据,并进行属性定制
/**
* gson: 通过JavaBean构建JSON数据,并进行属性定制
*/
public static void genJSONByBean01() {
JavaFile javaFile = new JavaFile();
javaFile.setFileName("test.java");
javaFile.setAuthor("devsong");
javaFile.setDescription("a demonstration of how to generate and parse JSON files");
javaFile.setCreateDate("2017-08-05");
javaFile.setFunction(new String[] { "genJSONFile", "parseJSONFile" });
javaFile.setMemberCount(1);
javaFile.setEncrypted(false);
System.out.println("
============ genJSONByBean01 =============");
GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting(); // 从方法名可以看出其功效: 设置漂亮的输出(对格式进行适当调整)[这一点类似Dom4j生成XML文件中格式的定制]
// 设置字段名策略
builder.setFieldNamingStrategy(new FieldNamingStrategy() {
public String translateName(Field f) {
// 将author字段名改为了大写
// 与bean类中 @SerializedName("FUN") 注解作用类似
if (f.getName().equals("author")) {
return "AUTHOR";
}
return f.getName();
}
});
// 由于javaFile的fileName属性用了transient关键字修饰,所以用gson转换为JSON数据时,fileName属性默认不会被转化到JSON
// 所以输出结果中不含fileName一项
Gson gson = builder.create();
System.out.println(gson.toJson(javaFile));
}
此处获取Gson对象不再通过new,而是通过GsonBuilder对象获取,而属性定制就是针对GsonBuilder,代码中的setPrettyPrinting()方法是对生成的JSON做了一个美化,使其有合理的缩进及换行,方便我们阅读。而setFieldNamingStrategy()设置字段名策略和第一例中的@SerializedName("FUN")注解方法一致。亮点3:将JSON解析直接对应到bean
/**
* 将JSON解析对应到Bean(即反解析)
*
* @throws IOException
*/
public static void parseJSON() throws IOException {
File file = new File("src/res/test.json");
String content = FileUtils.readFileToString(file, "utf-8");
System.out.println("
============ parse JSON to bean =============");
Gson gson = new Gson();
//可以传入多种类型参数,非常强大,例如能传入一个reader对象
JavaFile javaFile = gson.fromJson(content, JavaFile.class);
System.out.println(javaFile);
}
相较于JSON-java,这是一个非常方便的功能,能将解析的JSON直接对应到JavaBean,无需额外的操作,非常强大。亮点4:日期字符串直接解析对应到Java Date对象
开始之前,需要对JavaBean类做一点改动,也就是将createDate改为Date类型,然后更改相应的set和get方法。更改后存为新的类:JavaFile2.java,以下是作了修改的部分
package org.devsong.bean;
import java.util.Arrays;
import java.util.Date;
public class JavaFile2 {
... ...
private Date createDate; //将String类型改为实际的Date类型
... ...
public JavaFile2() {
}
... ...
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
... ...
}
/**
* gson: 解析JSON到Bean,并将日期字符串对应到日期类型
* @throws IOException
*/
public static void parseJSONWithDate() throws IOException {
File file = new File("src/res/test.json");
String content = FileUtils.readFileToString(file, "utf-8");
System.out.println("
============ parse JSON to bean with Date =============");
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); //注意此处获取Gson实例的步骤
JavaFile2 javaFile = gson.fromJson(content, JavaFile2.class);
System.out.println(javaFile.getCreateDate()); //已经将JSON内的字符串对应到了Date实例
}
前面说JSON本身是不支持Date格式的,JSON-java也没有提供相关功能,而Gson可以将日期字符串转化为Date类型对应到JavaBean实例,方便!亮点5: Java集合自动匹配
也就是说Gson会自动匹配集合类型对应到JavaBean。比如此例中若将JavaBean类的function字符串数组用list定义,那么在将JSON转化到JavaBean实例的时候,Gson会自动匹配集合ArrayList,很强大有木有!这个也很好理解,所以在这里不做例子了。
测试
在main方法里调用相关函数做功能测试
public static void main(String[] args) {
try {
genJSONByBean();
genJSONByBean01();
parseJSON();
parseJSONWithDate();
} catch (IOException e) {
e.printStackTrace();
}
}
运行结果如下:
============ genJSONByBean =============
{"author":"devsong","description":"a demonstration of how to generate and parse JSON files","createDate":"2017-08-05","FUN":["genJSONFile","parseJSONFile"],"memberCount":1,"encrypted":false}
============ genJSONByBean01 =============
{
"AUTHOR": "devsong",
"description": "a demonstration of how to generate and parse JSON files",
"createDate": "2017-08-05",
"FUN": [
"genJSONFile",
"parseJSONFile"
],
"memberCount": 1,
"encrypted": false
}
============ parse JSON to bean =============
JavaFile [fileName=null, author=devsong, description=a demonstration of how to generate and parse JSON files, createDate=2017-08-05, function=null, memberCount=1, encrypted=false]
============ parse JSON to bean with Date =============
Sat Aug 05 00:00:00 CST 2017
可见结果正常,斌且用transient标记的属性在生成与解析的过程都会被忽略,而且用@SerializedName注解标注的属性在解析时也必须需找到注解里面对应的名字才会解析到值。也就是function <--x--> FUN,二者在解析JSON时是不对应的。所以结果中function=null。