Java操作JSON
在这之前,我们需要 gson.jar 解析工具包,gson 是 Google解析JSON的一个开源框架,同类的还有 json.lib.jar 等等。
Gson提供了fromJson() 和toJson() 两个直接用于解析和生成的方法,前者实现反序列化,后者实现了序列化。
基本数据类型的解析以及生成
解析
Gson gson = new Gson();
int i = gson.fromJson("100", int.class); //100
double d = gson.fromJson("\"99.99\"", double.class); //99.99
boolean b = gson.fromJson("true", boolean.class); // true
String str = gson.fromJson("String", String.class); // String
生成
Gson gson = new Gson();
String jsonNumber = gson.toJson(100); // 100
String jsonBoolean = gson.toJson(false); // false
String jsonString = gson.toJson("String"); //"String"
POJO类(实体类)的解析以及生成
实体类的解析以及生成我们需要注意实体类中的属性以及JSON对象中的键是否一致,一致则方便转换。
实体类(User.java)
package com.li.entity;
public class User {
private String name;
private boolean flag;
private int age;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User(String name, boolean flag, int age, String address) {
super();
this.name = name;
this.flag = flag;
this.age = age;
this.address = address;
}
}
测试(操作前需要导入gson JAR包,并导入相应的类)
package com.li;
import com.google.gson.Gson;
import com.li.entity.User;
public class JSONTest {
public static void main(String[] args) {
Gson gson = new Gson();
/*实体类生成JSON:toJson*/
User user = new User("Li", true, 21, "HuNan");
String userJSON=gson.toJson(user);
System.out.println(userJSON);
/*JSON解析成实体类:fromJson,需要使用反射来了解其内部结构*/
User user01=gson.fromJson(userJSON, User.class);
System.out.println(user01.toString());
}
}
运行结果
{"name":"Li","flag":true,"age":21,"address":"HuNan"}
User [name=Li, flag=true, age=21, address=HuNan]
从上面POJO的生成与解析可以看出 Json 的字段和值的名称和POJO类成员属性名称是一一对应的。但是js在命名时一般采用下划线风格,而Java中一般采用的驼峰法(lowCamelCase),所以有时候可能会出现不和谐的情况。
例如我们稍稍改变一下JSON数据的字段名称:
/*将内部的双引号转义成普通意义*/
String userString="{\"name\":\"Li\",\"flag\":true,\"age\":21,\"add_ress\":\"HuNan\"}";
/*JSON解析成实体类:fromJson,需要使用反射来了解其内部结构*/
User user01=gson.fromJson(userString, User.class);
System.out.println(user01.toString());
输出:
User [name=Li, flag=true, age=21, address=null]
我们知道Gson在序列化和反序列化时需要使用反射,使用反射来查看类的内部结构,说到反射就不得不想到注解,一般各类库都将注解放到annotations包下,打开源码在com.google.gson包下果然有一个annotations,里面有一个SerializedName的注解类,这应该就是我们要找的。
那么对于json中email_address这个属性对应POJO的属性则变成:
@SerializedName("add_ress")
private String address;
这样的话,很好的保留了前端、后台各自的命名习惯。
但是又如果其它地方可以重用该类,其它字段都一样,就 address 字段不一样,又该如何呢?
我们可以为POJO字段提供备选属性名,SerializedName注解提供了两个属性,上面用到了其中一个,别外还有一个属性alternate,接收一个String数组。
@SerializedName(value="add_ress", alternate= {"addre_ss", "ad_dress"})
private String address;
当上面的三个属性(add_ress、addre_ss、ad_dress)都中出现任意一个时均可以得到正确的结果,但是此时如果用address则不行了。。。所以可以这样
@SerializedName(value="address", alternate= {"addre_ss", "ad_dress"})
private String address;
JSON 和 Map的转换
Gson gson = new Gson();
Map<String,String> map = new HashMap<String,String>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
String json=gson.toJson(map);
System.out.println(json);
Type type = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> map2 = gson.fromJson(json, type);
这个同时也涉及到 gson使用泛型的问题,因为我们在后面指定类型时 Map<String, String> 是行不通的。对于Java来说 Map<String, String>.class 和 Map.class 这俩个的字节码文件只一个那就是 Map.class,这是Java泛型使用时要注意的问题——泛型擦除。
为了解决的上面的问题,Gson为我们提供了TypeToken来实现对泛型的支持,所以应该像这样来指定类型:
Type type = new TypeToken<Map<String, String>>() {}.getType();