【前言】最近项目中对数据的解析都使用了Gson,艹蛋的是每次都要手写JavaBean对象很麻烦。抱着能否简化的心态阅读了Gson的部分源码。发现很多以前没有注意到的知识点。
写在前面的话
Gson是一个Java库,可用于将Java对象转换为JSON表示。它也可以用来将JSON字符串转换为等效的Java对象。 Gson可以使用任意Java对象,包括您没有源代码的预先存在的对象。
如何生成Json
JsonObject jsonObject = new JsonObject();
JsonObject jsonArray = new JsonObject();
//addProperty():产生JsonObject
jsonObject.addProperty("testString","7c");
jsonObject.addProperty("testInt",7);
jsonObject.addProperty("testBoolean",true);
jsonObject.addProperty("testCharater","7c");
jsonArray.addProperty("testStringArray","7c");
jsonArray.addProperty("testIntArray",7);
jsonArray.addProperty("testBooleanArray",true);
jsonObject.add("JsonElement",jsonArray);
System.out.println(jsonObject.toString());
//Json结构
{
"JsonElement": {
"testBooleanArray": true,
"testIntArray": 7,
"testStringArray": "7c"
},
"testBoolean": true,
"testCharater": "7c",
"testInt": 7,
"testString": "7c"
}
手动解析Json
try {
JSONObject originJson = new JSONObject(json);
ManualresolutionBean manualresolutionBean = new ManualresolutionBean();
boolean success = originJson.optBoolean("success");
int failCode = originJson.optInt("failCode");
JSONArray data = originJson.optJSONArray("data");
List<ManualresolutionBean.JsonList> jsonLists = new ArrayList();
manualresolutionBean.setFailCode(failCode);
manualresolutionBean.setSuccess(success);
for (int i = 0 ; i < data.length() ; i ++){
JSONObject jsonObject1 = data.getJSONObject(i);
JsonList jsonList = new JsonList();
String check = jsonObject1.optString("check");
String id = jsonObject1.optString("id");
int isPoor = jsonObject1.optInt("isPoor");
String level = jsonObject1.optString("level");
String model = jsonObject1.optString("model");
String name = jsonObject1.optString("name");
String pid = jsonObject1.optString("pid");
String sort = jsonObject1.optString("sort");
String supportPoor = jsonObject1.optString("supportPoor");
jsonList.setCheck(check);
jsonList.setId(id);
jsonList.setIsPoor(isPoor);
jsonList.setLevel(level);
jsonList.setModel(model);
jsonList.setName(name);
jsonList.setPid(pid);
jsonList.setSort(sort);
jsonList.setSupportPoor(supportPoor);
jsonLists.add(jsonList);
}
manualresolutionBean.setList(jsonLists);
} catch (JSONException e) {
e.printStackTrace();
}
}
【注意】
1.蓝色标记部分手动解析的时候充当JSONObject;【】充当JSONArray。
2.如果你放在main函数中执行会报 java.lang.RuntimeException: Stub!。这是因为你在Java中使用Android包出错。
通过构造器来创建Gson
new Gson()是我们日常开发中经常用来解析数据的方式。熟悉的人员请绕过这部分。
String jsonArray = "[\"U\",\"A\",\"R\",\"T\"]";
Gson gson = new Gson();
//json与数组的转换
//json转换为数组
String[] strings = gson.fromJson(jsonArray, String[].class);
for (String string : strings){
System.out.println(string);
}
//数组转换为json
String s = gson.toJson(strings);
System.out.println(s);
System.out.println("=================================");
//json转换为List
//new TypeToken<List<String>>(){}.getType()这里为什么需要一个{},查看TypeToken源码可以发现
//这样一段话TypeToken<List<String>> list = new TypeToken<List<String>>() {};
List<String> list = gson.fromJson(jsonArray, new TypeToken<List<String>>() {
}.getType());
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
//List转换为json
String listJson = gson.toJson(list,new TypeToken<List<String>>() {
}.getType());
System.out.println(listJson);
Gson用法进阶
属性重命名
- 场景:服务端返回的字段名,与我们本地的字段名有出入。
- 原理及用法:@SerializedName 指示此成员的注释应该以提供的名称值作为其字段名称序列化为JSON。
@SerializedName("name") String a;
@SerializedName(value="name1", alternate={"name2", "name3"}) String b;
@SerializedName(value ="name",alternate = {"userName","Name","Kobe"})
public String name;
String json = "{\"name\": \"Uart\"}";
String json1 = "{\"userName\": \"Uart\"}";
String json2 = "{\"Name\": \"Uart\"}";
String json3 = "{\"Kobe\": \"Uart\"}";
Gson gson = new Gson();
User user = gson.fromJson(json,new TypeToken<User>(){}.getType());
User user1 = gson.fromJson(json1,new TypeToken<User>(){}.getType());
User user2 = gson.fromJson(json2,new TypeToken<User>(){}.getType());
User user3 = gson.fromJson(json3,new TypeToken<User>(){}.getType());
user.toString();
user1.toString();
user2.toString();
user3.toString();
字段过滤
基于@Expose注解
- 原理:指示此成员的注释应公开给JSON序列化或反序列化。
- 注意 :此注释要生效必须调用GsonBuilder的excludeFieldsWithoutExposeAnnotation()方法。
- 用法:
@Expose private String firstName;
@Expose(serialize = false) private String lastName;
@Expose (serialize = false, deserialize = false) private String emailAddress;
private String password;
@Expose(serialize = true, deserialize = true) //序列化和反序列化都生效
private String a;
@Expose(serialize = false, deserialize = true) //序列化时不生效,反序列化时生效
private String b;
@Expose(serialize = true, deserialize = false) //序列化时生效,反序列化时不生效
private String c;
@Expose(serialize = false, deserialize = false) //序列化和反序列化都不生效,和不写注解一样
private String d;
private String e;
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
FilterBean filterBean = new FilterBean("A", "B", "C", "D", "E");
//序列化
System.out.println(gson.toJson(filterBean));
//反序列化
String json = "{\"a\":\"A\",\"b\":\"B\",\"c\":\"C\",\"d\":\"D\",\"e\":\"E\"}";
FilterBean filterBean1 = gson.fromJson(json, FilterBean.class);
System.out.println(filterBean1.toString());
//输出结果
{"a":"A","c":"C"}
FilterBean{a='A', b='B', c='null', d='null', e='null'}
基于版本
- 原理:@Since自成员或类型出现以来表示版本号的注释。
@Until表示直到成员或类型应该存在的版本号的注释。
此注释对于管理Web服务的Json类的版本控制非常有用。 - 注意:此注释要生效必须调用GsonBuilder的setVersion(double)方法。
- 用法:
@Since(1.0) private String emailAddress;
@Until(1.1) private String emailAddress;
@Since(1.3)
private String a;
@Until(1.9)
private String b;
@Since(1.5)
private String c;
@Until(1.2)
private String d;
private String e;
Gson gson = new GsonBuilder().setVersion(1.3).create();
FilterBean filterBean = new FilterBean("A", "B", "C", "D", "E");
//序列化
System.out.println(gson.toJson(filterBean));
//反序列化
String json = "{\"a\":\"A\",\"b\":\"B\",\"c\":\"C\",\"d\":\"D\",\"e\":\"E\"}";
FilterBean filterBean1 = gson.fromJson(json, FilterBean.class);
System.out.println(filterBean1.toString());
//输出结果
{"a":"A","b":"B","e":"E"}
FilterBean{a='A', b='B', c='null', d='null', e='E'}
基于访问修饰符
- 原理:excludeFieldsWithModifiers()方法,配置Gson以排除所有具有指定修饰符的类字段。默认,Gson将排除所有标记为瞬态(transient)或静态(static)的字段。此方法将覆盖该行为。
public String publicField = "public";
protected String protectedField = "protected";
private String privateField = "private";
String defaultField = "default";
final String finalField = "final";
static String staticField = "static";
Gson gson2 = new GsonBuilder().excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.PUBLIC).create();
ModifierBean modifierBean = new ModifierBean();
System.out.println(gson2.toJson(modifierBean));
//输出结果
{"protectedField":"protected","defaultField":"default","finalField":"final","staticField":"static"}
基于策略
- 原理:GsonBuilder 类包含 setExclusionStrategies(ExclusionStrategy... strategies)方法用于传入不定长参数的策略方法,用于直接排除指定字段名或者指定字段类型。
private String stringField;
private int intField;
private double doubleField;
public StrategiesBean(String stringField, int intField, double doubleField) {
this.stringField = stringField;
this.intField = intField;
this.doubleField = doubleField;
}
Gson gson3 = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes f) {
//排除指定字段名
return f.getName().equals("stringField");
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
//排除指定字段类型
return clazz.getName().equals(double.class.getName()); }}).create();
StrategiesBean strategies = new StrategiesBean("stringField", 111, 11.22);
System.out.println();
System.out.println(gson3.toJson(strategies));
String json2 = "{\"stringField\":\"stringField\",\"intField\":111,\"doubleField\":11.22}";
strategies = gson3.fromJson(json2, StrategiesBean.class);
System.out.println();
System.out.println(strategies);
//输出结果
{"intField":111}
Strategies{stringField='null', intField=111, doubleField=0.0}
感兴趣的小伙伴可以研究一下setFieldNamingStrategy()、addDeserializationExclusionStrategy()、addSerializationExclusionStrategy()这三种方法
自定义配置
输出null
对于 Gson 而言,在序列化时如果某个属性值为 null 的话,那么在序列化时该字段不会参与进来,如果想要显示输出该字段的话,可以通过 GsonBuilder 进行配置。
Gson gson3 = new GsonBuilder()
.serializeNulls()
.create();
StrategiesBean strategies = new StrategiesBean(null, 111, 11.22);
System.out.println();
System.out.println(gson3.toJson(strategies));
//输出结果
{"stringField":null,"intField":111,"doubleField":11.22}
格式化输出Json
Gson gson3 = new GsonBuilder()
.serializeNulls()
.setPrettyPrinting()//格式化输出
.create();
StrategiesBean strategies = new StrategiesBean(null, 111, 11.22);
System.out.println();
System.out.println(gson3.toJson(strategies));
//输出结果
{
"stringField": null,
"intField": 111,
"doubleField": 11.22
}
TypeAdapter
- 作用:将Java对象与JSON进行转换。默认情况下,Gson使用其内置类型将应用程序类转换为JSON适配器。如果Gson的默认JSON转换不适合某种类型,扩展此类以定制转换。
序列化交换位置
@Override
public void write(JsonWriter out, UartBean value) throws IOException {
//序列化过程
if(value == null) {
out.nullValue();
return;
}
String name = value.getName();
int age = value.getAge();
value.setName(age + "");
value.setAge(Integer.valueOf(name));
out.value(value.getName());
out.value(value.getAge());
}
@Override
public UartBean read(JsonReader in) throws IOException {
//反序列化过程
if(in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
in.beginObject();
UartBean uartBean = new UartBean();
while(in.hasNext()) {
switch (in.nextName()) {
case "name" :
uartBean.setName(in.nextString());
break;
case "age":
uartBean.setAge(in.nextInt());
break;
}
}
in.endObject();
return uartBean;
}
Gson gson4 = new GsonBuilder().registerTypeAdapter(UartBean.class, new UartAdapter()).create();
UartBean uartBean = new UartBean("10", 20);
System.out.println(gson4.toJson(uartBean));
String json4 = "{\"name\":\"10\",\"age\":24}";
UartBean bean = gson4.fromJson(json4, UartBean.class);
System.out.println();
System.out.println(bean);
//输出结果
"20" 10
UartBean{name='10', age=24}
JsonSerializer 和 JsonDeserializer是TypeAdapter提供的专门针对序列化和反序列化的接口。感兴趣的朋友可以自行了解一下。
更新
"156": "42F7B18E9C12ACFDC1B9",
"156": "3247F56244FDE",
针对上述json字符串如何解析?
stationNameInfoList = new ArrayList<EquivalentStationNameInfo>();
Iterator<String> keys = object.keys();
while (keys.hasNext()) {
EquivalentStationNameInfo equivalentStationNameInfo = new EquivalentStationNameInfo();
String next = keys.next();
equivalentStationNameInfo.setStationName(next);
equivalentStationNameInfo.setStationId(object.optString(next));
for (EquivalentHourInfo info : equivalentHourInfoList) {
if (info.getStationName().equals(next)) {
stationNameInfoList.add(equivalentStationNameInfo);
}
}
}