Gson的序列化功能
- 支持int, long, bool, string等基本類型
- 支持Url, 數(shù)組, atomic, Bean等各種類型
- List, Map類型也支持泛型
- 支持?jǐn)?shù)值Number的轉(zhuǎn)換規(guī)則
- 支持屬性命名下劃線/駝峰
- 支持過濾某寫屬性
- 支持自定義序列化adapter
遇到的問題
盡管gson足夠強(qiáng)大, 但是還是遇到了問題
譬如:
Map<String, Object> map = new HashMap<>();
map.put("int", 22);
map.put("long", 2211L);
float flo = 333.666f;
map.put("float", flo);
double dou = 122.55f;
map.put("double", dou);
map.put("bean", new TestBean("desc"));
String json = new Gson.toJson(map);
Map<String, Object> map2 = new Gson.fromJson(json, HashMap.class);
經(jīng)過序列化/反序列化,返回的map2, 如下效果:
16433549481788.jpg
可見, int, long, float全都轉(zhuǎn)成double; 而bean類被轉(zhuǎn)成LinkTreeMap
同理, list也是一樣的問題;
解決
可不可通過自定義gson序列化解決? 答案是可以的
通過自定義Gson gson = new GsonBuilder(), 然后設(shè)置
.registerTypeHierarchyAdapter(Map.class, new TypeAdapter<Map<String, Object>>()
和
.registerTypeHierarchyAdapter(Map.class, new TypeAdapter<Collection<String, Object>>()
即可
基本原理
定制gson的typeAdapter, 寫入時(shí)在數(shù)據(jù)頭部插入map/list的自身和item的class字典信息, 然后解析的時(shí)候解析字典, 反序列化正確的類型.
代碼如下
/**
* Created by Supylc on 2022/1/25.
* 自定義gson悯许,功能為:
* 1、能解析可變類型的Map, 例如Map<String, Object>
* 2、能正確解析Map里面的float,double波闹,long,int,short闭树,byte,char等
* 3荒澡、需要用GsonCasual類定制的Gson才能正確解析报辱,如果在本類寫入map,又用外部gson讀单山,則維持原解析效果
*
* 適用場(chǎng)景:
* 需要用到map或list序列化的地方(特別是item為可變類型)
*
* 注意:
* 用GsonCasual寫和讀碍现,不要出現(xiàn)用GsonCasual寫然后用其他Gson讀(反之亦然)讀情況
*/
public class GsonCasual {
private static final String TAG = "GsonCasual";
private static final String DICT = "__clz_dict__";
private static final Map<String, Class<?>> mClazzCacheMap = new HashMap<>();
/**
* 設(shè)置支持map和list的準(zhǔn)確解析幅疼,
* 原理:
* 1、自定義讀寫map和list
* 2鸵赫、在寫json的頭部衣屏,插入class字典,包含Map(或List)和key-value(或list-item)的類型信息
*
* 字典格式為:
* 數(shù)組大小辩棒,map(list)的類型class狼忱,value(map或list的item)的類型class
* 如:5,java.util.HashMap, java.lang.long, java.lang.Integer, java.lang.String
*
* 把字典信息獨(dú)立放在頭部一睁,對(duì)舊的map數(shù)據(jù)解析不做修改钻弄,容錯(cuò)性更好(比如不知情的情況下,用不同的gson進(jìn)行讀寫者吁,也不報(bào)錯(cuò))
*/
private static final Gson gson = new GsonBuilder()
//處理所有的map類型
.registerTypeHierarchyAdapter(Map.class, new TypeAdapter<Map<String, Object>>() {
@Override
public void write(JsonWriter out, Map<String, Object> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
Set<Map.Entry<String, Object>> entrySet = value.entrySet();
Object entryValue;
//在頭部寫一個(gè)class字典數(shù)據(jù)
writeClassDict(out, value);
for (Map.Entry<String, Object> entry: entrySet) {
entryValue = entry.getValue();
if (entryValue == null) {
out.name(entry.getKey()).nullValue();
} else {
out.name(entry.getKey()).jsonValue(gson.toJson(entryValue));
}
}
out.endObject();
}
@Override
public Map<String, Object> read(JsonReader in) throws IOException {
JsonToken jsonToken = in.peek();
if (jsonToken == JsonToken.NULL) {
return null;
}
Map<String, Object> result = null;
jsonToken = in.peek();
if (jsonToken != JsonToken.BEGIN_OBJECT) {
throw new NullPointerException("firstToken is wrong, gson invalid?");
}
in.beginObject();
int kvCount = 0;
String[] valueClazzArray = null;
while (in.hasNext()) {
String name = in.nextName();
Object readValue;
if (DICT.equals(name)) {
valueClazzArray = readDictObject(in);
continue;
}
if (result == null && valueClazzArray != null) {
result = (Map<String, Object>) newInstance(valueClazzArray[0]);
}
if (valueClazzArray != null) {
String valueClazz = valueClazzArray[kvCount + 1];
if (valueClazz == null) {
readValue = null;
} else {
readValue = gson.fromJson(in, getClazz(valueClazz));
}
kvCount ++;
} else {
readValue = gson.fromJson(in, String.class);
}
if (result == null) {
result = new HashMap<>();
}
result.put(name, readValue);
}
in.endObject();
if (result == null) {
result = new HashMap<>();
}
return result;
}
})
//處理所有的集合類型
.registerTypeHierarchyAdapter(Collection.class, new TypeAdapter<Collection<?>>() {
@Override
public void write(JsonWriter out, Collection<?> value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginArray();
//在頭部寫一個(gè)class字典數(shù)據(jù)
writeClassDict(out, value);
for (Object entry: value) {
if (entry == null) {
out.nullValue();
} else {
out.jsonValue(gson.toJson(entry));
}
}
out.endArray();
}
@Override
public Collection<?> read(JsonReader in) throws IOException {
JsonToken jsonToken = in.peek();
if (jsonToken == JsonToken.NULL) {
return null;
}
Collection<Object> result = null;
int kvCount = 0;
String[] valueClazzArray = null;
in.beginArray();
jsonToken = in.peek();
if (jsonToken == JsonToken.BEGIN_ARRAY) {
valueClazzArray = readDictObject(in);
}
while (in.hasNext()) {
Object readValue;
if (result == null && valueClazzArray != null) {
result = (Collection<Object>) newInstance(valueClazzArray[0]);
}
if (valueClazzArray != null) {
String valueClazz = valueClazzArray[kvCount + 1];
if (valueClazz == null) {
readValue = null;
} else {
readValue = gson.fromJson(in, getClazz(valueClazz));
}
kvCount ++;
} else {
readValue = gson.fromJson(in, String.class);
}
if (result == null) {
result = new ArrayList<>();
}
result.add(readValue);
}
in.endArray();
return result;
}
})
.create();
private static void writeClassDict(JsonWriter out, Object value) throws IOException {
if (value instanceof Map) {
Set<Map.Entry<String, Object>> entrySet = ((Map<String, Object>) value).entrySet();
out.name(DICT);
out.beginArray();
out.value(entrySet.size() + 1);
out.value(value.getClass().getName());
for (Map.Entry<String, Object> entry: entrySet) {
Object entryValue = entry.getValue();
if (entryValue == null) {
out.nullValue();
} else {
out.value(entryValue.getClass().getName());
}
}
out.endArray();
} else if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
out.beginArray();
out.value(collection.size() + 1);
out.value(value.getClass().getName());
for (Object entry: collection) {
if (entry == null) {
out.nullValue();
} else {
out.value(entry.getClass().getName());
}
}
out.endArray();
}
}
private static String[] readDictObject(JsonReader in) throws IOException {
int dictCount = 0;
String[] valueClazzArray = null; //包含root類型以及item類型
JsonToken jsonToken;
in.beginArray();
while (in.hasNext()) {
if (dictCount > 1) {
jsonToken = in.peek();
if (jsonToken == JsonToken.NULL) {
in.nextNull();
} else {
valueClazzArray[dictCount - 1] = in.nextString();
}
} else if (dictCount == 1) {
valueClazzArray[0] = in.nextString();
} else if (dictCount == 0) {
valueClazzArray = new String[in.nextInt()];
}
dictCount++;
}
in.endArray();
return valueClazzArray;
}
public static String toJson(Object src) {
return gson.toJson(src);
}
public static <T> T fromJson(String json, Class<T> clazz) {
return gson.fromJson(json, clazz);
}
private static Class<?> getClazz(String clazzName) {
Class<?> clazz = mClazzCacheMap.get(clazzName);
if (clazz == null) {
try {
clazz = Class.forName(clazzName);
} catch (Exception e) {
log("getClazz, not found class: " + clazzName);
e.printStackTrace();
}
}
return clazz;
}
private static Object newInstance(String clazz) {
try {
return getClazz(clazz).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static void log(String log) {
Log.i(TAG, log);
}
用例
只需要用 GsonCasual.toJson/fromJson 即可, 使用也很簡(jiǎn)單
總結(jié)
原想用parcel方式解決, 因?yàn)閜arcel序列化更直接,更節(jié)省空間
用記錄class字典的方式, parcel也能實(shí)現(xiàn), 但最后寫出來, 其實(shí)也是走gson走過的路, 因此何必重復(fù)造輪子? 明顯的, parcel不適合用在此場(chǎng)景選擇gson的自定義實(shí)現(xiàn), 效率也很高, 實(shí)現(xiàn)成本更低