Gson是一个可以实现Java对象和JSON格式数据进行相互转换的Java库。
dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
}
导火线
最近项目需要做多语言,多语言由后台统一配置,请求数据结构如下:
{
"code": 200,
"msg": "",
"data": {
"title": "lang_title",
"desc": "lang_desc",
"lang": {
"en-US": {
"lang_title": "This is the title",
"lang_desc": "This is the description"
},
"zh-CN": {
"lang_title": "这是标题",
"lang_desc": "这是描述"
},
"ko-KR": {
"lang_title": "이것은 제목입니다",
"lang_desc": "이것은 설명입니다"
},
"ja-JP": {
"lang_title": "これがタイトルです",
"lang_desc": "これが説明です"
}
}
}
}
因为此处不能简单的定义类和对应的字段来接收数据,因此需要借助JsonElement来接收lang对应的JSON对象,但又想用自己定义的Language类对象接收:
public class Response {
public int code;
public String msg;
public Data data;
public static class Data {
public String title;
public String desc;
public Language lang;
}
}
public static class Language{}
但是这样解析的时候会没数据,必须保存lang对应的JSON对象的字符串或者JsonElement/JsonObject,于是想到使用Gson的高级用法。
TypeAdapterFactory
public interface TypeAdapterFactory {
<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
}
通过实现TypeAdapterFactory接口,可以根据不同的类型返回不同的TypeAdapter:
public static class LanguageTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final Class<T> rawType = (Class<T>) type.getRawType();
if (!Language.class.isAssignableFrom(rawType)) {
return null;
}
final TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this,
type);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
//delegateAdapter.write(out, value);
//Language对象转Json逻辑
}
@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
Language language = new Language();
//return (T) delegateAdapter.read(in);
//Json转Language对象逻辑
return (T) language;
}
};
}
}
TypeAdapter
public abstract class TypeAdapter<T> {
/**
* 写入一个JSON值(数组, 对象, 字符串, 数值, 布尔值或空值)
*
* @param value 写入JSON的Java对象,可空
*/
public abstract void write(JsonWriter out, T value) throws IOException;
/**
* 将传入的值转换成JSON
* 此方法相比于Gson的toJson方法更为严格,顶层必须是对象或者数组,并且数值类型调用对应的isNaN和isInfinite方法必须为false。而Gson的toJson方法顶层可以是任何类型并且数值可以是NaN/NEGATIVE_INFINITY/POSITIVE_INFINITY
* @since 2.2
*/
public final void toJson(Writer out, T value) throws IOException {
JsonWriter writer = new JsonWriter(out);
write(writer, value);
}
/**
* 调用此方法以后,在自定义的类型适配器中不用手动处理null值
*/
public final TypeAdapter<T> nullSafe() {
return new TypeAdapter<T>() {
@Override public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
} else {
TypeAdapter.this.write(out, value);
}
}
@Override public T read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return TypeAdapter.this.read(reader);
}
};
}
/**
* 将传入的值转换成JSON字符串,此方法相比于Gson的toJson方法更为严格,顶层必须是对象或者数组,并且数值类型调用对应的isNaN和isInfinite方法必须为false
* @since 2.2
*/
public final String toJson(T value) {
StringWriter stringWriter = new StringWriter();
try {
toJson(stringWriter, value);
} catch (IOException e) {
throw new AssertionError(e); // No I/O writing to a StringWriter.
}
return stringWriter.toString();
}
/**
* 将传入的值转换成JSON树结构,可传入空值
*
* @since 2.2
*/
public final JsonElement toJsonTree(T value) {
try {
JsonTreeWriter jsonWriter = new JsonTreeWriter();
write(jsonWriter, value);
return jsonWriter.get();
} catch (IOException e) {
throw new JsonIOException(e);
}
}
/**
* 读取一个JSON值(数组,对象,字符串,数值,布尔值或空值),并转换成Java对象返回
*
* @return 转换后的Java对象. 可空
*/
public abstract T read(JsonReader in) throws IOException;
/**
* 将传入的JSON转换成Java对象.
* 此方法比Gson的fromJson方法更为严格:
* 1. 流不能以不执行前缀")]}'\n"开头
* 2. 每个流顶层必须只能有一个值,不能有多个顶层值
* 3. 顶层值必须是对象或数组,不能是其他值
* 4. 数值不能是NaN/NEGATIVE_INFINITY/POSITIVE_INFINITY
* 5. 行尾注释不能以//或#开头,以换行符结束
* 6. 注释不能以/*开头和*/结尾
* 7. 不支持未加双引号或单引号的名称值
* 8. 不支持未加双引号或单引号的字符串值
* 9. 不支持数组元素之间用;分割,只能用,分割
* 10.不支持数组元素出现空值导致分隔符重复
* 11.不支持名称和值用=或=>分割,只能用:分割
* 12.不支持名称/值对之间用;分割,只能用,分割
*
* @since 2.2
*/
public final T fromJson(Reader in) throws IOException {
JsonReader reader = new JsonReader(in);
return read(reader);
}
/**
* 将传入的JSON字符串转换成Java对象.
*
* @since 2.2
*/
public final T fromJson(String json) throws IOException {
return fromJson(new StringReader(json));
}
/**
* 将传入的JSON树结构转换成Java对象. 可传入JsonNull
*
* @since 2.2
*/
public final T fromJsonTree(JsonElement jsonTree) {
try {
JsonReader jsonReader = new JsonTreeReader(jsonTree);
return read(jsonReader);
} catch (IOException e) {
throw new JsonIOException(e);
}
}
}
从源码可以看出来当需要将JSON转换成Java对象的时候会调用read方法,当需要将Java对象转换成JSON的时候需要调用wirte方法。
public class Language {
public static class LanguageTypeAdapter extends TypeAdapter<Language> {
@Override
public void write(JsonWriter out, Language value) throws IOException {
out.beginObject();
for (int i = 0; i < value.map.size(); i++) {
String name = value.map.keyAt(i);
ArrayMap<String, String> m = value.map.valueAt(i);
out.name(name);
out.beginObject();
for (int j = 0; j < m.size(); j++) {
out.name(m.keyAt(j)).value(m.valueAt(j));
}
out.endObject();
}
out.endObject();
}
@Override
public Language read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
Language language = new Language();
String langType = null;
in.beginObject();
while (in.hasNext()) {
JsonToken jsonToken = in.peek();
if (JsonToken.NAME == jsonToken) {
langType = in.nextName();
language.putLanguageType(langType);
} else if (JsonToken.BEGIN_OBJECT == jsonToken) {
String keyName = null;
in.beginObject();
while (in.hasNext()) {
JsonToken token = in.peek();
if (JsonToken.NAME == token) {
keyName = in.nextName();
} else if (JsonToken.STRING == token) {
language.putLanguage(langType, keyName, in.nextString());
}
}
in.endObject();
}
}
return language;
}
}
private ArrayMap<String, ArrayMap<String, String>> map = new ArrayMap<>();
public Language() {
}
public void putLanguageType(String langType) {
map.put(langType, new ArrayMap<>(2));
}
public void putLanguage(String langType, String name, String value) {
ArrayMap<String, String> m = map.get(langType);
m.put(name, value);
}
}
注册类型适配器:
Gson gson = new GsonBuilder().registerTypeAdapter(Language.class, new Language.LanguageTypeAdapter().nullSafe()).create();
@JsonAdapter
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface JsonAdapter {
/** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link JsonDeserializer} or {@link JsonSerializer}. */
Class<?> value();
/** false, to be able to handle {@code null} values within the adapter, default value is true. */
boolean nullSafe() default true;
}
通过注释可以给类或者属性指定解析方式:
public class Response {
public int code;
public String msg;
public Data data;
public static class Data {
public String title;
public String desc;
@JsonAdapter(value = Language.LanguageTypeAdapter.class)
public Language lang;
}
}
每个类或者属性只能添加一个JsonAdapter注释
JsonDeserializer
public interface JsonDeserializer<T> {
/**
* 当Gson遇到指定类型的字段时,它会在反序列化期间调用此回调方法
*
* @param 需要反序列化的Json数据
* @param 需要反序列化的对象的类型
* @return
* @throws
*/
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException;
}
从源码可以看出来,当Gson反序列化JSON到指定类型的字段时,会调用deserialize方法并传入对应的JSON树结构数据。
public class Language {
ArrayMap<String, ArrayMap<String, String>> map = new ArrayMap<>(1);
public Language() {
}
public String translate(String langType, String keyName) {
ArrayMap<String, String> lang = map.get(langType);
return lang.get(keyName);
}
public static class LanguageDeserializer implements JsonDeserializer<Language> {
@Override
public Language deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
//{"en-US":{"lang_title":"This is the title","lang_desc":"This is the description"},"zh-CN":{"lang_title":"这是标题","lang_desc":"这是描述"},"ko-KR":{"lang_title":"이것은 제목입니다","lang_desc":"이것은 설명입니다"},"ja-JP":{"lang_title":"これがタイトルです","lang_desc":"これが説明です"}}
Language language = new Language();
JsonObject jsonObject = json.getAsJsonObject();
Set<String> langTypes = jsonObject.keySet();
for (String langType : langTypes) {
ArrayMap<String, String> langMap = new ArrayMap<>(2);
language.map.put(langType, langMap);
JsonObject langObject = jsonObject.get(langType).getAsJsonObject();
Set<String> langList = jsonObject.keySet();
for (String lang : langList) {
langMap.put(lang, langObject.get(lang).getAsString());
}
}
return null;
}
}
}
JsonDeserializer是Json的反序列化器,当默认的反序列化不满足要求的时候可以自定义反序列化器。当Gson遇到指定类型的字段时,会在反序列化期间调用deserialize方法。
注册反序列化器:
Gson gson = new GsonBuilder().registerTypeAdapter(Language.class, new Language.LanguageDeserializer()).create();
JsonSerializer
public interface JsonSerializer<T> {
/**
* Gson invokes this call-back method during serialization when it encounters a field of the
* specified type.
*
* <p>In the implementation of this call-back method, you should consider invoking
* {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any
* non-trivial field of the {@code src} object. However, you should never invoke it on the
* {@code src} object itself since that will cause an infinite loop (Gson will call your
* call-back method again).</p>
*
* @param src the object that needs to be converted to Json.
* @param typeOfSrc the actual type (fully genericized version) of the source object.
* @return a JsonElement corresponding to the specified object.
*/
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
}
从源码可以看出来,当Gson序列化指定类型时,会调用serialize方法并传入对应的对象数据。
public class Language {
ArrayMap<String, ArrayMap<String, String>> map;
public Language() {
}
public String translate(String langType, String keyName) {
ArrayMap<String, String> lang = map.get(langType);
return lang.get(keyName);
}
public static class LanguageSerializer implements JsonSerializer<Language> {
@Override
public JsonElement serialize(Language src, Type typeOfSrc, JsonSerializationContext context) {
ArrayMap<String, ArrayMap<String, String>> map = src.map;
JsonObject jsonObject = new JsonObject();
for (int i = 0; i < map.size(); i++) {
String langType = map.keyAt(i);
ArrayMap<String, String> lang = map.valueAt(i);
JsonObject langObj = new JsonObject();
jsonObject.add(langType, langObj);
for (int j = 0; j < lang.size(); j++) {
langObj.addProperty(lang.keyAt(j), lang.valueAt(j));
}
}
return jsonObject;
}
}
}
以上就是为了解决项目中遇到的问题作出了不同的尝试以后总结出来的几种方式。
感谢大家的支持,如有错误请指正,如需转载请标明原文出处!