Gson的序列化功能
- 支持int, long, bool, string等基本类型
- 支持Url, 数组, atomic, Bean等各种类型
- List, Map类型也支持泛型
- 支持数值Number的转换规则
- 支持属性命名下划线/驼峰
- 支持过滤某写属性
- 支持自定义序列化adapter
遇到的问题
尽管gson足够强大, 但是还是遇到了问题
譬如:
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);
经过序列化/反序列化,返回的map2, 如下效果:

可见, int, long, float全都转成double; 而bean类被转成LinkTreeMap
同理, list也是一样的问题;
解决
可不可通过自定义gson序列化解决? 答案是可以的
通过自定义Gson gson = new GsonBuilder(), 然后设置
.registerTypeHierarchyAdapter(Map.class, new TypeAdapter<Map<String, Object>>()
和
.registerTypeHierarchyAdapter(Map.class, new TypeAdapter<Collection<String, Object>>()
即可
基本原理
定制gson的typeAdapter, 写入时在数据头部插入map/list的自身和item的class字典信息, 然后解析的时候解析字典, 反序列化正确的类型.
代码如下
/**
* 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读,则维持原解析效果
*
* 适用场景:
* 需要用到map或list序列化的地方(特别是item为可变类型)
*
* 注意:
* 用GsonCasual写和读,不要出现用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<>();
/**
* 设置支持map和list的准确解析,
* 原理:
* 1、自定义读写map和list
* 2、在写json的头部,插入class字典,包含Map(或List)和key-value(或list-item)的类型信息
*
* 字典格式为:
* 数组大小,map(list)的类型class,value(map或list的item)的类型class
* 如:5,java.util.HashMap, java.lang.long, java.lang.Integer, java.lang.String
*
* 把字典信息独立放在头部,对旧的map数据解析不做修改,容错性更好(比如不知情的情况下,用不同的gson进行读写,也不报错)
*/
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;
//在头部写一个class字典数据
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();
//在头部写一个class字典数据
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 即可, 使用也很简单
总结
-
原想用parcel方式解决, 因为parcel序列化更直接,更节省空间
用记录class字典的方式, parcel也能实现, 但最后写出来, 其实也是走gson走过的路, 因此何必重复造轮子? 明显的, parcel不适合用在此场景 -
选择gson的自定义实现, 效率也很高, 实现成本更低
网友评论