Gson 解析之 - 如何让 json 键和实体类的属性名不一样
最近Gson用的比较多,用的时候一直有一个疑问,难道本地的实体类的属性名一定要和Json的键一一对应吗?
json数据
{
"name": "wangzhen",
"age": 20,
"tall": "176",
"sex": "male"
}
对应的实体类
public class Person1 {
String name;
String age;
String sex;
String tall;
}
一切看起来都是那么的美好,但是如果有一天,我们从另一个接口上,请求下来的数据是这样的呢?
{
"person_name": "wangzhen",
"person_age": 20,
"tall": "176",
"person_sex": "male"
}
那我们的实体类岂不是要写成这样?
public class Person2 {
String first_name;
String person_name;
String person_age;
String person_sex;
}
可以试一下Person1
Gson gson = new Gson();
InputStream inputStream = null;
inputStream = new FileInputStream(new File("json file path"));
InStream2String converter = new InStream2String();
String json = converter.convert(inputStream);
Person person = gson.fromJson(json, Person1.class);
System.out.println(person);
=== output ===
Person1{name='null', age='null', sex='null', tall='176'}
简直不能忍
可是有什么办法吗?想到的一个解决方案就是为每一个实体类的属性添加一个注解,在注解中标明该属性对应json的值,比如上面的 json 的 person_name 应该对应 Person 的 name 属性。
有思路就简单咯,自己动手丰衣足食
新建一个注解,就叫Map吧
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Map {
String value();
}
现在的属性应该是这个样子的,有点感觉了。
@Map("person_name") String name;
@Map("person_age") String age;
@Map("person_sex") String sex;
在运行时,只需要动态获取注解里面的值,就可以得到当前属性应当对应的json的键,所以就可以起到属性名与json键不同的效果了
好了,只需要去 818 Gson的源码,然后用一个装饰模式为Gson动态的添加一个职责差不多咯
但是! too young, too naive!
跟踪源码的时候发现,会调用
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory
这个类中的 这个方法
private String getFieldName(Field f) {
SerializedName serializedName = f.getAnnotation(SerializedName.class);
return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
}
尼玛,这 SerializedName
又是什么鬼。。
点进去看一下
SerializedName
注解的源码:
/**
* An annotation that indicates this member should be serialized to JSON with
* the provided name value as its field name.
*
* This annotation will override any {@link com.google.gson.FieldNamingPolicy}, including
* the default field naming policy, that may have been set on the {@link com.google.gson.Gson}
* instance. A different naming policy can set using the {@code GsonBuilder} class. See
* {@link com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)}
* for more information.
*
* Here is an example of how this annotation is meant to be used:
*
* public class SomeClassWithFields {
* @SerializedName("name") private final String someField;
* private final String someOtherField;
*
* public SomeClassWithFields(String a, String b) {
* this.someField = a;
* this.someOtherField = b;
* }
* }
*
*
* The following shows the output that is generated when serializing an instance of the
* above example class:
*
* SomeClassWithFields objectToSerialize = new SomeClassWithFields("a", "b");
* Gson gson = new Gson();
* String jsonRepresentation = gson.toJson(objectToSerialize);
* System.out.println(jsonRepresentation);
*
* ===== OUTPUT =====
* {"name":"a","someOtherField":"b"}
*
*
* NOTE: The value you specify in this annotation must be a valid JSON field name.
*
* @see com.google.gson.FieldNamingPolicy
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
/**
* @return the desired name of the field when it is serialized
*/
String value();
}
这。。。。。
结合上面的getFieldName
方法,Gson在获取属性名的时候会先判断SerializedName
注解的值,如果有就使用 SerializedName
的值(value)作为属性名。
好了,轮子造到一半让Google给截胡了。但,省了很多事啊哈哈哈哈哈哈哈哈。
忽略上面的笑声
好了,你的实体类的属性现在应该长这模样,至于其他的,小伙伴们一起探索咯
@SerializedName("person_name") String name;
@SerializedName("person_age") String age;
@SerializedName("person_sex") String sex;