Gson?So easy.
1.概述
这篇文章主要讲述了Gson的使用.包括从最基础的基本类型的序列化,到对象,数组,集合,再到Gson注解,Gson Builder,再到格式化,自定义序列化与反序列化等内容.
另外文章篇幅较长,建议挑选所需部分查看.所有例子都提供了完整源码,在文章的后面.
2.Gson是什么?
(1)JSON
JSON全称为JavaScript Object Notation,一种轻量级的数据交换格式.
类似于XML但比XML更小,更易解析.
(2)Gson
Gson是Google提供的可以使Java对象与JSON互转的类库,可将Java对象转换为JSON,也可将JSON转换成Java对象.
(3)Gson的好处
- a.容易,高效,强大:Gson是Google管理的标准化库,经过高度优化,同时api简单,比如fromJSON(),toJSON().
- b.无依赖性:不需要其他库,当然jdk除外.
- c.结果简单:转换成的json易于阅读.
- d.支持泛型,支持内部类.
- e.开源,免费提供.
3.配置Gson环境
目前最新的是2.8.6版本.
(1)Gradle
dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
}
(2)Maven
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
(3)jar
4.Gson基础
(1)基础类型
int intJSON = 1;
long longJSON = 1L;
double doubleJSON = 1.0;
float floatJSON = 1.0f;
byte byteJSON = 1;
char charJSON = 'G';
short shortJSON = 1;
boolean boooleanJSON = true;
System.out.println(gson.toJson(intJSON));
System.out.println(gson.toJson(longJSON));
System.out.println(gson.toJson(doubleJSON));
System.out.println(gson.toJson(floatJSON));
System.out.println(gson.toJson(byteJSON));
System.out.println(gson.toJson(charJSON));
System.out.println(gson.toJson(shortJSON));
System.out.println(gson.toJson(boooleanJSON));
System.out.println("----------------------------------------");
System.out.println(gson.fromJson("1",Integer.class));
System.out.println(gson.fromJson("1.0",Double.class));
System.out.println(gson.fromJson("1",Long.class));
System.out.println(gson.fromJson("true",Boolean.class));
System.out.println(gson.fromJson("B",Character.class));
就是对应输出,没啥好说的.
(2)嵌套对象
Gson gson = new Gson();
System.out.println(gson.toJson(new A()));
String str = "{'field':'gggg','field2':33,'field3':'G','field4':'true'}";
A a = gson.fromJson(str,A.class);
System.out.println(a.getField1());
System.out.println(a.getField2());
System.out.println(a.getField3());
System.out.println(a.getField4());
A类:
class A
{
private String field1 = "123";
private int field2 = 1;
private char field3 = 'X';
private Boolean field4 = true;
}
序列化出来的对象用{}表示.
反序列化时,注意格式,注意名字对应,用单引号引起来,还有char会自动变为String类型,另外对于布尔类型可以加单引号或不加单引号,都可以正常反序列化.
(3)数组
a.普通数组
int [] a = new int []{1,2,3};
double [] b = new double []{1.0,2.0,3.0};
String [] c = new String []{"123","456"};
System.out.println(gson.toJson(a));
System.out.println(gson.toJson(b));
System.out.println(gson.toJson(c));
int [] aa = gson.fromJson("[6,7,8]",int [].class);
double [] bb = gson.fromJson("[6.0,8.0,9.0]",double [].class);
String [] cc = gson.fromJson("['123123','5464']",String [].class);
System.out.println(Arrays.toString(aa));
System.out.println(Arrays.toString(bb));
System.out.println(Arrays.toString(cc));
对普通数组的话,{}变成了[].
还有就是反序列化时,默认会在逗号后面添一个空格.
b.List
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
System.out.println(gson.toJson(list));
List<AAAA> list2 = new ArrayList<>();
list2.add(new AAAA());
list2.add(new AAAA("45345",8888,false));
System.out.println(gson.toJson(list2));
System.out.println("---------------------------------");
Type type = new TypeToken<List<String>>(){}.getType();
String strList = "['234234','45457']";
List<String> list3 = gson.fromJson(strList, type);
System.out.println(list3);
String strList2 = "[" +
"{'field1':'9999','field2':666,'field3':'true'}" +
"," +
"{'field1':'sdlkfkl','field2':-234234,'field3':'false'}" +
"]";
List<AAAA> list4 = gson.fromJson(strList2, new TypeToken<List<AAAA>>(){}.getType());
System.out.println(list4);
class AAAA
{
private String field1 = "123";
private int field2 = 5;
private boolean field3 = true;
}
序列化没什么问题,直接toJson即可,反序列化时,需要配合java.lang.reflect.Type使用,明确指出所要转换的类型:
Type type = new TypeToken<List<String>>(){}.getType();
TypeToken<T>中T为所需要的类型,再把这个Type对象传递给fromJson即可完成转换.
List序列化出来的用[]表示.
(4)Map
Map<String,Integer> map = new HashMap<>();
map.put("123",88);
map.put("2349",999);
System.out.println(gson.toJson(map));
String str = "{'123':23423423,'9999':-234234}";
Map<String,Integer> map2 = gson.fromJson(str, new TypeToken<Map<String,Integer>>(){}.getType());
System.out.println(map2.get("123").toString());
System.out.println(map2.get("9999").toString());
Map同样序列化时直接toJson,反序列化时使用java.lang.reflect.Type.Map序列化出来的用{}表示.
(5)Set
Set<String> set = new HashSet<>();
set.add("123123");
set.add("2349594");
set.add("-234()@#$@#");
System.out.println(gson.toJson(set));
String str = "['38483','@*#$(@#$)','SD<FGDF>G']";
Set<String> set2 = gson.fromJson(str, new TypeToken<Set<String>>(){}.getType());
set2.stream().forEach(System.out::println);
set序列化出来的用[]表示.
(6)null
System.out.println(gson.toJson(new AAA()));
String str = "{'field2':333,'field3':null}";
System.out.println(gson.fromJson(str, AAA.class));
class AAA
{
private String field1 = null;
private Integer field2 = null;
private Double field3 = 3.0;
@Override
public String toString()
{
return "field1:"+field1+",field2:"+field2+",field3:"+field3;
}
}
Gson会忽略空值,在序列化时看不到null对应的键值对,反序列化时,直接对应为空.
(7)混合
一个对象里面包含了List,Map,Set,null.
System.out.println(gson.toJson(new Test()));
class Test
{
private List<String> listField = new ArrayList<>();
private Map<String,Double> mapField = new HashMap<>();
private Set<Integer> setField = new HashSet<>();
private List<User> userListFiled = new ArrayList<>();
private Map<String,User> userMapField = new HashMap<>();
private Set<User> userSetField = new HashSet<>();
private Set<String> nullSetField = null;
private Map<String,User> nullMapField = null;
private List<User> nullListField = null;
private Long longField = 23423423423L;
private int intField = 234234;
private Double doubleField = 234234.23423;
private User userField = new User();
{
listField.add("234234");
mapField.put("23432",-234.0);
setField.add(-23423);
userListFiled.add(new User("3459",-23423,new int []{4,5,5}));
userListFiled.add(new User());
userMapField.put("()",new User());
userMapField.put("------", new User("345345",3434,new int []{4,44}));
userSetField.add(new User());
userSetField.add(new User());
}
}
class User
{
private String name = "noName";
private int age = 0;
private int [] nums = new int[]{2,3,4};
public User()
{
}
public User(String name,int age,int [] nums)
{
this.name = name;
this.age = age;
this.nums = nums;
}
}
这个就不反序列化了,可以看到对象,Map用{},List,Set用[].与上面的一致.
5.Gson注解
(1)@SerializedName
SerializedName注解有两个参数,分别是value与alternate.SerializedName由注解名字可以知道与序列化成的Json名字有关.
默认情况下,json的键名与对象的字段名一致,@SerializedName可以解决序列化/反序列化时json键名与对象字段名不一致的问题,使其将json可以正确映射到对应的字段.
a.单独使用value
单独使用一个参数时,即@SerializedName("xxx")或@SerializedName(value="xxx"),序列化时,对象字段会变为@SerializedName中的值,反序列化时,若不是@SerializedName()中的值则不会反序列化:
public class SerializedNameValueTest
{
public static void main(String[] args) {
Gson gson = new Gson();
System.out.println(gson.toJson(new SerializedNameValueTest().new User()));
String str = "{'this is a name':'what????','this is an age':13,'email':'33333@222.com'}";
System.out.println(gson.fromJson(str,User.class));
String str2 = "{'name':'askldfklaslk','age':-222,'email':'234234@23423.com'}";
System.out.println(gson.fromJson(str2, User.class));
}
class User
{
@SerializedName("this is a name")
private String name = "123";
@SerializedName("this is an age")
private int age = 0;
private String email = "xxx@xxx.com";
private double[] nums = new double[] { 1.0, 2.0 };
@Override
public String toString() {
return "name:" + name + ",age:" + age + ",eamil:" + email + ",nums:" + Arrays.toString(nums);
}
}
}
最后一个反序列化不成功,因为'name'不对应@SerializedName()中的'this is a name'.
b.同时使用value与alternate
同时使用两者可以解决上面的问题,即name字段--->json中的'this is a name'--->name字段:
public class SerializedNameValueAndAlternateTest
{
public static void main(String[] args) {
Gson gson = new Gson();
System.out.println(gson.toJson(new SerializedNameValueAndAlternateTest().new User()));
String str = "{'this is a name':'what????','this is an age':13,'email':'33333@222.com'}";
System.out.println(gson.fromJson(str, User.class));
String str2 = "{'name':'askldfklaslk','age':-222,'email':'234234@23423.com'}";
System.out.println(gson.fromJson(str2, User.class));
}
class User {
@SerializedName(value = "this is a name",alternate = "name")
private String name = "123";
@SerializedName(value = "this is an age",alternate = "age")
private int age = 0;
private String email = "xxx@xxx.com";
private double[] nums = new double[] { 1.0, 2.0 };
@Override
public String toString() {
return "name:" + name + ",age:" + age + ",eamil:" + email + ",nums:" + Arrays.toString(nums);
}
}
}
alternate就是为反序列化准备的,若找不到value中对应的值,则寻找alternate对应的值,找不到再设为null.
若同时存在value与alternate:
String str3 = "{'name':'altername','this is a name':'value'}";
System.out.println(gson.fromJson(str3, User.class));
String str4 = "{'this is a name':'value','name':'altername'}";
System.out.println(gson.fromJson(str4, User.class));
则以"最晚"出现的值为标准.
(2)@Expose
@Expose可以忽略某个字段,有两个参数:
- serialize
- deserialize
默认情况下都是true,分别表示序列化与反序列化.
System.out.println(gson.toJson(new User()));
class User
{
@Expose(serialize = false)
private String name = "123";
@Expose(deserialize = false)
private int age = 0;
@Expose(serialize = false,deserialize = false)
private String email = "xxx@xxx.com";
@Expose(serialize = true,deserialize = true)
private double [] nums = new double [] {1.0,2.0};
}
name不允许序列化,age不允许反序列化,email不允许序列化与反序列化,nums既允许也允许反序列化.(此时效果等于没加@Expose)
输出:
咦?好像没用的样子?
因为Gson对象会默认忽略@Expose,想要@Expose生效需要使用Gson Builder:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.excludeFieldsWithoutExposeAnnotation();
Gson gson = gsonBuilder.create();
System.out.println(gson.toJson(new User()));
String str = "{'name':'234','age':-3,'email':'23423','nums':[3,4,3]}";
System.out.println(gson.fromJson(str, User.class));
class User
{
@Expose(serialize = false)
private String name = "123";
@Expose(deserialize = false)
private int age = 0;
@Expose(serialize = false,deserialize = false)
private String email = "xxx@xxx.com";
@Expose(serialize = true,deserialize = true)
private double [] nums = new double [] {1.0,2.0};
@Override
public String toString()
{
return "name:"+name+",age:"+age+",eamil:"+email+",nums:"+Arrays.toString(nums);
}
}
不能反序列化age与email,输出的是age与email的默认值.
6.Gson Builder
前面的例子基本上都是通过
Gson gson = new Gson();
来直接实例化一个Gson来使用Gson的,使用Gson Builder可以设置Gson的某些属性,使用其中的create()返回一个Gson.
(1)基础
创建一个简单的Gson,通过create()创建:
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
(2)命名规则
a.使用系统命名规则
使用
gsonBuilder.setFieldNamingPolicy();
设置json中键的命名规则.6个值可选:
- IDENTITY:相同,json中的键名与字段名相同.
- LOWER_CASE_WITH_DASHES:在原来字段的大写字母前加-,并且把大写变成小写
- LOWER_CASE_WITH_DOTS:在原来字段的大写字母前加.,并且把大写变成小写
- LOWER_CASE_WITH_UNDERSCORES:在原来字段的大写字母前加_,并且把大写变成小写
- UPPER_CAMEL_CASE:首字母大写
- UPPER_CAMEL_CASE_WITH_SPACES:在原来字段的大写字母前加空格
public class NamingRulesTest
{
public static void main(String[] args) {
NamingRulesTest n = new NamingRulesTest();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY);
System.out.println(gsonBuilder.create().toJson(n.new User()));
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES);
System.out.println(gsonBuilder.create().toJson(n.new User()));
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DOTS);
System.out.println(gsonBuilder.create().toJson(n.new User()));
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
System.out.println(gsonBuilder.create().toJson(n.new User()));
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
System.out.println(gsonBuilder.create().toJson(n.new User()));
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
System.out.println(gsonBuilder.create().toJson(n.new User()));
}
class User
{
@SuppressWarnings("unused")
private String Name_Name_name = "123";
@SuppressWarnings("unused")
private int _age_age_Age_age = 645;
@SuppressWarnings("unused")
private double numsNumsNums = 34.45;
}
}
注意,若某个字段有了@SerializedName,则这个字段遵循@SerializedName的策略.
b.自定义命名规则
重写FieldNamingStrategy中的translateName(Field field),把自定义的FieldNamingStrategy传递给GsonBuilder的setFieldNamingStrategy().
比如想要前缀加上某人的名字:
public class CustomNamingRulesTest
{
public static void main(String[] args) {
FieldNamingStrategy myNamingStrategy = new FieldNamingStrategy(){
@Override
public String translateName(Field field) {
return "kr"+field.getName();
}
};
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingStrategy(myNamingStrategy);
Gson gson = gsonBuilder.create();
System.out.println(gson.toJson(new CustomNamingRulesTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private String name = "123";
@SuppressWarnings("unused")
private int num = 5;
}
}
(3)null
默认情况下,Gson实例不允许序列化null,如果想要序列化null,借助GsonBuilder的serializeNulls()方法:
public class NullTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeNulls();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE_WITH_SPACES);
System.out.println(gsonBuilder.create().toJson(new NullTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private String thisIsANullField;
@SuppressWarnings("unused")
private Integer andThisIsANullFieldToo;
}
}
(4)排除
前面已经接触到了@Expose这样的排除策略,下面看看更加强大的配合GsonBuilder使用的排除策略.主要有四种:属性名排除,类型排除,修饰符排除,@Expose排除.
a.属性名排除
public class ExclusionNameTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeNulls();
ExclusionStrategy exclusionName = new ExclusionStrategy(){
@Override
public boolean shouldSkipField(FieldAttributes f)
{
return f.getName().endsWith("e");
}
@Override
public boolean shouldSkipClass(Class<?> arg0) {
return false;
}
};
gsonBuilder.setExclusionStrategies(exclusionName);
System.out.println(gsonBuilder.create().toJson(new ExclusionNameTest().new User()));
}
class User
{
private String name;
private int num;
}
}
排除字段名以e结尾的字段.下面是重点:
public boolean shouldSkipField(FieldAttributes f)
{
return f.getName().endsWith("e");
}
重写的shouldSkipField从名字可以看出跳过某些字段,返回true表示跳过,即排除这个字段.上面的例子中若名字以e结尾则跳过,因此输出:
b.类型排除
public class ExclusionTypeTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeNulls();
ExclusionStrategy exclusionType = new ExclusionStrategy(){
@Override
public boolean shouldSkipField(FieldAttributes arg0) {
return false;
}
@Override
public boolean shouldSkipClass(Class<?> cls) {
return cls == String.class;
}
};
gsonBuilder.setExclusionStrategies(exclusionType);
System.out.println(gsonBuilder.create().toJson(new ExclusionNameTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private String name;
@SuppressWarnings("unused")
private int num;
}
}
重写的shouldSkipClass表示要跳过的类,这里跳过了String,只剩下num.
@Override
public boolean shouldSkipClass(Class<?> cls) {
return cls == String.class;
}
c.修饰符排除
这个不用重写方法了,直接使用GsonBuilder的excludeFieldsWithModifiers(),参数是java.lang.reflect.Modifier:
这个可选的比较多就不一一列举了,只选了一个final的例子:
public class ExclusionModifierTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.excludeFieldsWithModifiers(Modifier.FINAL);
System.out.println(gsonBuilder.create().toJson(new User()));
}
}
class User
{
@SuppressWarnings("unused")
private final String name = "123";
@SuppressWarnings("unused")
private static int num;
}
d.@Expose排除
这个准确来说是排除没有被@Expose注解的字段:
public class ExclusionExposeTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.excludeFieldsWithoutExposeAnnotation();
System.out.println(gsonBuilder.create().toJson(new ExclusionExposeTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private String name;
@SuppressWarnings("unused")
private int num;
@Expose(serialize = true,deserialize = true)
private int age;
}
}
(5)Lenient
这是有关于反序列化时JSON的容错机制的问题,因为通常来说将一个对象通过Gson转换成json没什么大问题,但是将json转换为对象的时候就...就难说了,因为不知道是否符合标准的json格式,因此Gson提供了一定的容错机制,就是Lenient.
Lenient翻译过来是"宽容的"的意思,可以通过:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLenient();
开启.
Gson内部有一个JsonReader类,默认情况下,JsonReader是严格的且仅接受符合RFC 4627标准的json(RFC 4627标准可以看看这里),设置为lenient后可以"容忍"以下几种错误:
- a.以 ) ] } ' \n 开头
- b.多个顶层值.
- c.任何类型的顶层值
- d.数字可能是NaNs或infinites
- e.以行注释//或#结尾
- f.以一个换行符结束
- g.C风格的注释/**/,可能会嵌套
- h.键/字符串没有引号或者单引号
- i.数组元素以;分隔
- j.不必要的数组分隔符,"默认"null为省略值,比如[1,,2],"默认"第二个元素为null
- k.键值以=或=>分隔而非使用:
- l.键值对以;分隔而非使用,
设置setLenient()后,Gson会尽可能解析有错误的json,若实在无能为力,会抛出MalformedJsonException异常.
下面是一个不严格的json的例子:
public class LenientTest
{
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLenient();
String str = "{'name'='234';'num'=6}";
System.out.println(gsonBuilder.create().fromJson(str,User.class));
}
class User
{
private String name;
private int num;
@Override
public String toString()
{
return "name:"+name+",num:"+num;
}
}
}
下面是一个异常的例子:
String errorStr = "{'name'=????,,,,,,}";
System.out.println(gsonBuilder.create().fromJson(errorStr,User.class));
(6)Floats & Doubles
先看一个例子:
public class FloatTest
{
public static void main(String[] args) {
Gson gson = new Gson();
System.out.println(gson.toJson(new FloatTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private float f = Float.POSITIVE_INFINITY;
}
}
会报错:
它说Infinity在JSON标准是一个非法值.想要重写这个行为需要使用GsonBuilder.serializeSpecialFloatingPointValues().
因为JSON规范不允许NaN,-Infinity,Infinity,因此会报错.下面使用GsonBuilder:
public class FloatTest
{
public static void main(String[] args) {
// Gson gson = new Gson();
// System.out.println(gson.toJson(new FloatTest().new User()));
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeSpecialFloatingPointValues();
System.out.println(gsonBuilder.create().toJson(new FloatTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private float f = Float.POSITIVE_INFINITY;
}
}
直接输出Infinity:
double也类似:
public class DoubleTest
{
public static void main(String[] args)
{
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeSpecialFloatingPointValues();
System.out.println(gsonBuilder.create().toJson(new DoubleTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private double ni = Double.NEGATIVE_INFINITY;
@SuppressWarnings("unused")
private double pi = Double.POSITIVE_INFINITY;
@SuppressWarnings("unused")
private double nan = Double.NaN;
}
}
(7)模型版本化
通过@Since与@Until添加版本控制,控制某个版本在序列化与反序列化时忽略或忽略某个字段.
@Since表示从某个版本开始这个字段不忽略,@Until表示这个版本后将忽略该字段.
需要配合GsonBuilder的setVersion使用,设定版本号.
public class VersionTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeNulls();
System.out.println(gsonBuilder.setVersion(0.9).create().toJson(new VersionTest().new User()));
System.out.println(gsonBuilder.setVersion(1.0).create().toJson(new VersionTest().new User()));
System.out.println(gsonBuilder.setVersion(1.4).create().toJson(new VersionTest().new User()));
System.out.println(gsonBuilder.setVersion(1.5).create().toJson(new VersionTest().new User()));
}
class User
{
@Since(1.0)
private String name;
@Until(1.4)
private int num;
}
}
@Since的范围包含了左区间端点,@Until的范围不包含右区间端点.
(8)格式化日期
可以使用setDateForamt()来格式化日期输出:
gsonBuilder.setDateFormat(int style);
gsonBuilder.setDateFormat(String pattern);
gsonBuilder.setDateFormat(int dateStyle,int timeStyle);
第一个函数的参数为DateFormat中的常量值,第二个函数的参数是类似SimpleDateFormat中的String,第三个函数的参数与第一个类似,分开日期与时间设置.
再说以下setPrettyPrinting()这个函数,看名字就知道,美化打印的.会加上空格.
public class FormatTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setPrettyPrinting();
System.out.println(gsonBuilder.setDateFormat(DateFormat.SHORT, DateFormat.SHORT).create().toJson(new FormatTest().new User()));
System.out.println(gsonBuilder.setDateFormat(DateFormat.SHORT,DateFormat.LONG).create().toJson(new FormatTest().new User()));
System.out.println(gsonBuilder.setDateFormat(DateFormat.MEDIUM, DateFormat.MEDIUM).create().toJson(new FormatTest().new User()));
System.out.println(gsonBuilder.setDateFormat(DateFormat.LONG, DateFormat.MEDIUM).create().toJson(new FormatTest().new User()));
System.out.println(gsonBuilder.setDateFormat(DateFormat.FULL, DateFormat.FULL).create().toJson(new FormatTest().new User()));
System.out.println(gsonBuilder.setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(new FormatTest().new User()));
}
class User
{
@SuppressWarnings("unused")
private Date date = new Date();
}
}
7.Gson进阶
(1)枚举
枚举其实也与普通的字段类似,序列化与反序列化正常操作.
public class EnumTest
{
public static void main(String[] args) {
Gson gson = new Gson();
System.out.println(gson.toJson(new EnumTest().new User()));
String str = "{\"season\":\"SUMMER\",\"name\":\"8888\"}";
System.out.println(gson.fromJson(str,User.class));
}
enum Season
{
SPRING,
SUMMER,
AUTUMN,
WINTER
}
class User
{
private Season season = Season.SPRING;
private String name = "123";
@Override
public String toString()
{
return "season:"+season.toString()+",name:"+name;
}
}
}
主要配合@SerializedName()使用,例如对于月份,可以使用1,2,3来表示:
public class EnumTest
{
public static void main(String[] args) {
Gson gson = new Gson();
System.out.println(gson.toJson(new EnumTest().new User()));
String str = "{\"season\":\"SUMMER\",\"name\":\"8888\",\"month\":\"2\"}";
System.out.println(gson.fromJson(str,User.class));
}
enum Season
{
SPRING,
SUMMER,
AUTUMN,
WINTER
}
class User
{
private Season season = Season.SPRING;
private String name = "123";
private Month month = Month.January;
@Override
public String toString()
{
return "season:"+season.toString()+",name:"+name+",month:"+month.toString();
}
}
enum Month
{
@SerializedName("1")
January,
@SerializedName("2")
February,
@SerializedName("3")
March,
@SerializedName("4")
April,
@SerializedName("5")
May,
@SerializedName("6")
June,
@SerializedName("7")
July,
@SerializedName("8")
August,
@SerializedName("9")
September,
@SerializedName("10")
October,
@SerializedName("11")
November,
@SerializedName("12")
December
}
}
(2)泛型
这里主要说一下反序列化泛型.因为序列化泛型...直接toJson()就好了.
想想这样的情景:有一个待解析的Json String,类型为List,因此想有一个这样的方法:
public <T> List<T> fromJSON(String json,Class<T> cls);
好了,怎么写呢,之前用的是TypeToken()实现反序列化:
List<Integer> integerList = new ArrayList<>();
integerList.add(3);
integerList.add(-999);
gson.toJson(integerList, new TypeToken<List<Integer>>() {}.getType());
答案就是使用ParameterizedType:重写里面的getActualTypeArguments(),getOwnerTpye()与getRawType():
public class GenericsTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
List<Integer> integerList = new ArrayList<>();
integerList.add(3);
integerList.add(-999);
gson.toJson(integerList, new TypeToken<List<Integer>>() {}.getType());
List<String> stringList = new ArrayList<>();
stringList.add("12321");
stringList.add("(@)#$@#");
String t1 = gson.toJson(integerList);
String t2 = gson.toJson(stringList);
new GenericsTest().fromJSON(t1, Integer.class).stream().forEach(System.out::println);
new GenericsTest().fromJSON(t2, String.class).stream().forEach(System.out::println);
}
public <T> List<T> fromJSON(String json,Class<T> cls)
{
return new Gson().fromJson(json,new ParameterizedTypeImpl(cls));
}
class ParameterizedTypeImpl implements ParameterizedType
{
private Class cls;
public ParameterizedTypeImpl(Class cls)
{
this.cls = cls;
}
@Override
public Type[] getActualTypeArguments()
{
return new Type[]{cls};
}
@Override
public Type getRawType()
{
return List.class;
}
@Override
public Type getOwnerType()
{
return null;
}
}
}
@Override
public Type[] getActualTypeArguments()
{
return new Type[]{cls};
}
返回实际参数类型数组,在这里是String.class与Integer.class.
@Override
public Type getRawType()
{
return List.class;
}
返回声明这个类型的类或接口,在这里是List.class.
@Override
public Type getOwnerType()
{
return null;
}
返回其成员之一的类型,就是说如果完全限定类名为A.B,则返回A,在这里没有A,因此为null.
定义好实现ParameterizedTpye接口的类后,把它传递给fromJson()作为第二参数,构造方法的参数为List<T>中的T.class.
(4)自定义序列化
自定义序列化一般用于自定义简化json.
比如有一个User类:
class User
{
private String name = "123";
private String email = "xxx@xxx.com";
private int [] nums = new int [] {1,2,3};
}
想要不序列化name,可以使用@Expose(serialize = false),但是想要部分序列化nums,比如只是需要第一个nums[0],上面的@Expose,@SerializedName等注解都用不上,这时需要使用JsonSerializer自定义序列化,重写其中的:
public JsonElement serialize(T t,Type type,JsonSerializationContext context)
可以返回一个JsonObject,这里的JsonObject可以自定义添加属性(即键值对).
需要配合GsonBuilder使用,创建了自己的JsonSerializer<T>后,把它传递给GsonBuilder的registerTypeAdapter():
public class SerializeCustomTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
JsonSerializer<User> serializer = new JsonSerializer<SerializeCustomTest.User>() {
@Override
public JsonElement serialize(User u,Type type,JsonSerializationContext context)
{
JsonObject object = new JsonObject();
object.addProperty("name",u.getName());
object.addProperty("email",u.getEmail());
object.addProperty("nums",u.getNums()[0]);
return object;
}
};
gsonBuilder.registerTypeAdapter(User.class,serializer);
System.out.println(gsonBuilder.create().toJson(new SerializeCustomTest().new User()));
}
class User
{
private String name = "123";
private String email = "xxx@xxx.com";
private int [] nums = new int [] {1,2,3};
public int [] getNums()
{
return nums;
}
public String getName()
{
return name;
}
public String getEmail()
{
return email;
}
}
}
(5)自定义反序列化
自定义反序列化主要就是针对有多余键值对的json,比如User只需要name字段,但是传来的json包含了诸如age这样的键,因此只需挑选所需的进行反序列化.与序列化类似,首先定义自己的JsonDeserializer<T>,重写其中的:
public T deserialize(JsonElement,Type,JsonDeserializationContext);
返回一个自定义的反序列化的对象.最后在GsonBuilder中registerTypeAdapter()即可.
public class DeserializedCustomTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
JsonDeserializer<User> deserializer = new JsonDeserializer<DeserializedCustomTest.User>() {
@Override
public User deserialize(JsonElement json,Type type,JsonDeserializationContext context)
{
JsonObject object = json.getAsJsonObject();
User user = new DeserializedCustomTest().new User(object.get("name").getAsString());
return user;
}
};
gsonBuilder.registerTypeAdapter(User.class, deserializer);
String str = "{\"aaa\":\"bbbb\",\"name\":\"this is a name\",\"age\":\"444\"}";
System.out.println(gsonBuilder.create().fromJson(str,User.class));
}
class User
{
private String name;
private int age;
public User(String name)
{
this.name = name;
}
@Override
public String toString()
{
return "name:"+name+",age:"+age;
}
}
}
(6)默认实例
可以设置反序列化时的默认值,通过构造方法实现.比如json中没有所需要的字段的值,默认情况下为null,如果想要不为null,可以设定默认值,对于对象可以设定构造方法.
通过实现InstanceCreator<T>来实现,重写其中的createInstance方法,再配合GsonBuilder的registerTypeAdapter().
public class InstanceCustomTest
{
public static void main(String[] args) {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(
AA.class,
new InstanceCustomTest().new AAInstance(
new InstanceCustomTest().new BB("123","456")
)
);
String str = "{\"name\":\"123123\"}";
System.out.println(gsonBuilder.create().fromJson(str, AA.class));
}
class AAInstance implements InstanceCreator<AA>
{
private BB bb;
public AAInstance(BB bb)
{
this.bb = bb;
}
@Override
public AA createInstance(Type type)
{
return new AA(bb);
}
}
class AA
{
private String name = "AA";
private BB bb;
public AA(BB bb)
{
this.bb = bb;
}
@Override
public String toString()
{
return "name:"+name+",bb:"+bb.toString();
}
}
class BB
{
private String field1;
private String field2;
public BB(String field1,String field2)
{
this.field1 = field1;
this.field2 = field2;
}
@Override
public String toString()
{
return "field1:"+field1+",field2:"+field2;
}
}
}
json没有为BB类设定值,采用默认值.
(7)@JsonAdapter
这个是自定义序列化/反序列化的注解,可以简化JsonSerializer与JsonDeserializer.另外不用配合GsonBuilder使用,直接使用new Gson().toJson()/fromJson()即可,比直接使用JsonSerializer与JsonDeserializer要简单.
首先使一个类实现JsonSerializer<T>接口,接着把这个类作为要注解的字段的@JsonAdapter的参数:
class Serializer implements JsonSerializer<User>
class ContainUser
{
private String userId = "x";
@JsonAdapter(Serializer.class)
private User user = new User();
}
public class JsonAdapterSerializeTest
{
public static void main(String[] args)
{
Gson gson = new Gson();
System.out.println(gson.toJson(new JsonAdapterSerializeTest().new ContainUser()));
}
class Serializer implements JsonSerializer<User>
{
@Override
public JsonElement serialize(User u,Type type,JsonSerializationContext context)
{
JsonObject object = new JsonObject();
object.addProperty("name",u.getName());
object.addProperty("email",u.getEmail());
object.addProperty("nums",u.getNums()[0]);
return object;
}
}
class ContainUser
{
private String userId = "x";
@JsonAdapter(Serializer.class)
private User user = new User();
}
class User
{
private String name = "123";
private String email = "xxx@xxx.com";
private int [] nums = new int [] {1,2,3};
public int [] getNums()
{
return nums;
}
public String getName()
{
return name;
}
public String getEmail()
{
return email;
}
}
}
反序列化也类似.
public class JsonAdapterDeserializeTest
{
public static void main(String[] args) {
Gson gson = new Gson();
String str = "{\"aaaa\":\"bbbb\",\"name\":\"this is a name\",\"age\":\"444\"}";
System.out.println(gson.fromJson(str,User.class));
}
class Deserialize implements JsonDeserializer<User>
{
@Override
public User deserialize(JsonElement json,Type type,JsonDeserializationContext context)
{
JsonObject object = json.getAsJsonObject();
User user = new JsonAdapterDeserializeTest().new User(object.get("name").getAsString());
return user;
}
}
@JsonAdapter(Deserialize.class)
class User
{
private String name = "123";
public User()
{
}
public User(String name)
{
this.name = name;
}
@Override
public String toString()
{
return "name:"+name;
}
}
}
实现JsonDeserializer<T>,重写deserialize(),在所需的类上添加注解.
注意@JsonAdapter注解只能添加在类上.
8.源码
所有例子的完整源码.
github.
码云.
Gson官方github.
9.参考链接
1.Gson Tutorial Series by Future Studio
2.gson完全教程
3.gson教程
4.gson用户指南
5.Gson User guide
6.gson
Gson?So easy.的更多相关文章
- 让复杂Json数据和对象自由转换 --- Gson
Gson是谷歌用于对Json操作的库,里面有着强大而又方便的功能,最常用的就是 fromJson():将json数据转化为对象: toJson():将对象转化为json数据! 对于普通的json数据使 ...
- 一起来开发Android的天气软件(四)——使用Gson解析数据
离上一篇文章过去才4.5天,我们赶紧趁热打铁继续完毕该系列的天气软件的开发. 承接上一章的内容使用Volley实现网络的通信.返回给我们的是这一串Json数据{"weatherinfo&qu ...
- Android中的应用——谷歌官方Json分析工具Gson使用
一个.Gson基本介绍 Gson(又称Google Gson)是Google公司公布的一个开放源码的Java库.主要用途为串行化Java对象为JSON字符串,或反串行化JSON字符串成Java对象. ...
- [转]用GSON 五招之内搞定任何JSON数组
关于GSON的入门级使用,这里就不提了,如有需要可以看这篇博文 <Google Gson的使用方法,实现Json结构的相互转换> ,写的很好,通俗易懂. 我为什么写这篇文章呢?因为前几晚跟 ...
- [转] Android:用GSON 五招之内搞定任何JSON数组
[From] http://www.open-open.com/lib/view/open1472632967912.html 写在前面 关于GSON的入门级使用,这里就不提了,如有需要可以看这篇博文 ...
- Gson把对象转成json格式的字符串
近期在做一个java web service项目,须要用到jason,本人对java不是特别精通,于是開始搜索一些java平台的json类库. 发现了google的gson.由于之前对于protoco ...
- JSON介绍及Android最全面解析方法(Gson、AS自带org.son、Jackson解析)
前言 今天,我们来介绍一下现今主流的数据交换格式-JSON! 相同作为主流为数据交换格式-XML,假设有兴趣能够阅读我写的XML及其DOM.SAX.PULL解析方法和对照 文件夹 定义 JavaScr ...
- android使用Gson来解析json
Gson是一种对象的解析json,非常好用,介绍一个站点http://json.parser.online.fr/能够帮我们看一个字符串是不是Json 对于Json文件 { "id" ...
- No-args constructor for class X does not exist. Register an InstanceCreator with Gson for this type to fix this problem.
Gson解析JSON字符串时出现了下面的错误: No-args constructor for class X does not exist. Register an InstanceCreator ...
随机推荐
- Docker Tips: 关于/var/run/docker.sock
本文转载自Docker Tips: 关于/var/run/docker.sock 导语 你可能已经运行过docker hub上的container并且注意到其中的一些需要绑定挂载(mount)/var ...
- Typescript快速入门
目录 什么是Typescript 为什么学习Typescript 快速搭建开发环境 1.安装node.js 2.使用node自带的npm安装Typescript编译器 3.配置vscode编辑环境 4 ...
- Information:java: javacTask: 源发行版 8 需要目标发行版 1.8
原文链接:https://blog.csdn.net/idiot_qi/article/details/105149846 创建新maven项目跑main,出现这个编译异常 网上找了找,记录一下以备不 ...
- oracle ORA-00257
su - oracle sqlplus /nolog conn / as sysdba select * from v$flash_recovery_area_usage; select sum(pe ...
- # PyComCAD介绍及开发方法
项目地址:https://github.com/JohnYang1210/PycomCAD 1.综述 提到Autocad在工业界的二次开发,VB或者Lisp可能作为常用的传统的编程语言.但是,Py ...
- Tango with django 1.9 中文——2.准备工作
在正式开始写代码之前,设置好开发环境是非常重要的.你要确保所有必须的组件都已安装好.本章将概述五个你需要了解的关键组件的设置和使用.清单如下: 使用命令行 Python Python包管理器pip和虚 ...
- .NET并发编程-数据并行
本系列学习在.NET中的并发并行编程模式,实战技巧 内容目录 数据并行Fork/Join模式PLINQ 本小节开始学习数据并行的概念模式,以及在.NET中数据并行的实现方式.本系列保证最少代码呈现量, ...
- 盘点Excel中的那些有趣的“bug”
本文由葡萄城技术团队原创并首发 转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. Excel 1.0早在1985年正式进入市场,距今已经有36年了,虽然在推出 ...
- C# 应用 - 多线程 5) 死锁
两个线程中的每一个线程都尝试锁定另外一个线程已锁定的资源时,就会发生死锁. 两个线程都不能继续执行. 托管线程处理类的许多方法都提供了超时设定,有助于检测死锁. 例如,下面的代码尝试在 lockObj ...
- Java中的Set集合
Set接口简介 Set接口和List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,它是比Collecti ...