关于Gson无法将匿名类转化为json字符串的问题
在使用gson过程中,一般会将数据存在一个对象模型中,使用gson将模型转换成json字符串用于数据交互。
代码形如:
ArrayList<String> list = new ArrayList<>();
list.add("test1");
list.add("test2");
Gson gson = new Gson();
System.out.println("list to json is : "+gson.toJson(list));
程序输出内容为:
list to json is : ["test1","test2"]
但是当转化对象是匿名类时:
ArrayList<String> arrayList = new ArrayList<String>() {{
add("对");
add("不对");
}};
Gson gson = new Gson();
System.out.println("isAnonymousClass list to json is : "+gson.toJson(arrayList));
程序输出内容为:
isAnonymousClass list to json is : null
同理,如果使用模型对象,模型的某个字段的实例对象为匿名类,这个字段序列化的结果也是null。
这里我们研究一下gson的实现来分析原因:
首先,通过gson.toJson方法的实现很容易招到com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type, com.google.gson.stream.JsonWriter) 这个方法,实现如下:
/**
* Writes the JSON representation of {@code src} of type {@code typeOfSrc} to
* {@code writer}.
* @throws JsonIOException if there was a problem writing to the writer
*/
@SuppressWarnings("unchecked")
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
boolean oldLenient = writer.isLenient();
writer.setLenient(true);
boolean oldHtmlSafe = writer.isHtmlSafe();
writer.setHtmlSafe(htmlSafe);
boolean oldSerializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(serializeNulls);
try {
((TypeAdapter<Object>) adapter).write(writer, src);
} catch (IOException e) {
throw new JsonIOException(e);
} finally {
writer.setLenient(oldLenient);
writer.setHtmlSafe(oldHtmlSafe);
writer.setSerializeNulls(oldSerializeNulls);
}
}
进入getAdapter方法
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
TypeAdapter<?> cached = typeTokenCache.get(type);
if (cached != null) {
return (TypeAdapter<T>) cached;
}
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
boolean requiresThreadLocalCleanup = false;
if (threadCalls == null) {
threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
calls.set(threadCalls);
requiresThreadLocalCleanup = true;
}
// the key and value type parameters always agree
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
try {
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
threadCalls.put(type, call);
for (TypeAdapterFactory factory : factories) {
TypeAdapter<T> candidate = factory.create(this, type);
if (candidate != null) {
call.setDelegate(candidate);
typeTokenCache.put(type, candidate);
return candidate;
}
}
throw new IllegalArgumentException("GSON cannot handle " + type);
} finally {
threadCalls.remove(type);
if (requiresThreadLocalCleanup) {
calls.remove();
}
}
}
这里,获取adapter时,会从默认的工厂列表中逐个获取工厂,基于传入的类型获取adapter实例,
这里我们关注的是com.google.gson.internal.Excluder这个工厂,
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
Class<?> rawType = type.getRawType();
final boolean skipSerialize = excludeClass(rawType, true);
final boolean skipDeserialize = excludeClass(rawType, false);
if (!skipSerialize && !skipDeserialize) {
return null;
}
return new TypeAdapter<T>() {
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
private TypeAdapter<T> delegate;
@Override public T read(JsonReader in) throws IOException {
if (skipDeserialize) {
in.skipValue();
return null;
}
return delegate().read(in);
}
@Override public void write(JsonWriter out, T value) throws IOException {
if (skipSerialize) {
out.nullValue();
return;
}
delegate().write(out, value);
}
private TypeAdapter<T> delegate() {
TypeAdapter<T> d = delegate;
return d != null
? d
: (delegate = gson.getDelegateAdapter(Excluder.this, type));
}
};
}
这里
public boolean excludeClass(Class<?> clazz, boolean serialize) {
if (version != Excluder.IGNORE_VERSIONS
&& !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
return true;
}
if (!serializeInnerClasses && isInnerClass(clazz)) {
return true;
}
if (isAnonymousOrLocal(clazz)) {
return true;
}
List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
for (ExclusionStrategy exclusionStrategy : list) {
if (exclusionStrategy.shouldSkipClass(clazz)) {
return true;
}
}
return false;
}
这里调用了isAnonymousOrLocal这个方法(从方法名称来看就是判断是否是匿名类或本地类,其判断是否是匿名类的逻辑是使用的java.lang.Class#isAnonymousClass方法,根据类名判断),由于skipSerialize和skipDeserialize 都由这个逻辑返回了true,最后create方法返回了Excluder工厂生成adapter对象.
由Excluder的注释和返回的TypeAdapter对象都很容易看出,它的作用就是忽略该对象的j转换,最终返回null
关于Gson无法将匿名类转化为json字符串的问题的更多相关文章
- 使用DataContractJsonSerializer类将类型实例序列化为JSON字符串和反序列化为实例对象 分类: JSON 前端 2014-11-10 10:20 97人阅读 评论(1) 收藏
一.JSON简介 JSON(JavaScript Object Notation,JavaScript对象表示法)是一种轻量级的数据交换格式. JSON是"名值对"的集合.结构由大 ...
- 如何将C#对象转化为JSON字符串
System.Web.Extensions.dll中类JavaScriptSerializer可以帮助我们把C#对象转化为JSON字符串. 有一个Person类 public class Person ...
- python字典转化成json格式。JSONEncoder和JSONDecoder两个类来实现Json字符串和dict类型数据的互相转换
遇到问题:进行Webservice接口测试时,对接口入参数据进行了处理,变成了dict格式,去进行接口请求报错. 需要转成成json格式,双引号去扩. 如下: 更改代码: # 在Python标准库的j ...
- js如何生成一个对象,并转化为json字符串
js如何生成一个对象,并转化为json字符串,很多人都会误写为: var ary = []; var obj = {}; for (var i = 0; i < 3; i++) { obj.na ...
- 前台的js对象数组传到后台处理。在前台把js对象数组转化为json字符串,在后台把json字符串解析为List<>
前台的js对象数组传到后台处理.在前台把js对象数组转化为json字符串,在后台把json字符串解析为List<>
- json字符串转化为json对象and 对象转化为 json字符串
第一种方法: var data =evel('('+jsonstr+')') 解析: 这种方法是常用的方法, 即动态执行 javascript代码 在堆中存放数据. 存在安全问题. 第二种方法: ...
- 将数组转化为json字符串(不使用json_encode函数)
将数组转化为json字符串(不使用json_encode函数) public function arrayToJson($arr,$jsonStr=''){ $jsonStr.='{'; foreac ...
- js中JSON的解析(将json字符串转化为对象)和序列化(将对象转化为json字符串)(函数的功能一般都挺全的,需要的时候去查看完整函数)
js中JSON的解析(将json字符串转化为对象)和序列化(将对象转化为json字符串)(函数的功能一般都挺全的,需要的时候去查看完整函数) 一.总结 1.JSON解析:JSON.parse(myJS ...
- JS对象转化为JSON字符串
js方法: JSON.stringify 把一个对象转换成json字符串 JSON.parse 把一个json字符串解析成对象. 实例: var jsObj = {}; jsObj.testArray ...
随机推荐
- python调用虹软2.0第三版
这一版,对虹软的功能进行了一些封装,添加了人脸特征比对,比对结果保存到文件,和从文件提取特征进行比对,大体功能基本都已经实现,可以进行下一步的应用开发了 face_class.py from ctyp ...
- Linux - 命令重定向
命令重定向, 就是将目前得到的数据转移到指定的地方.分为以下几种: >>>1>2>1>>2>>< 1. > 与 >>先看一 ...
- Codeforces 496D - Tennis Game
496D - Tennis Game 思路:枚举每个t,求出对应的满足条件的s. 代码: #include<bits/stdc++.h> using namespace std; #def ...
- Unity寻路的动态烘焙
随着Unity5.6的推出,我们终于迎来了NavMesh的动态烘培,我们期待已久的功能终于来了,不用再研究A*算法了,话说改进的网格寻路更加方便高效. 网址:https://blog.csdn.net ...
- Greengenes Database(16S)
The Greengenes Database Release 13_5 这是16S的一个非常重要的数据库 The Greengenes Database, a public resource sin ...
- 3-23 Rspec自动化测试(开始练习)
闰年程序 leap_year_spec.rb require_relative './leap_year' describe "Leap Year" do it "201 ...
- codeforces 1042d//Petya and Array// Codeforces Round #510 (Div. 2)
题意:给出一个数组,求其中和小于t的区间数. 先计算前缀和数组sum[i].对当前的sum[i],查询树状数组中有几个比(sum[i]-t)大的数,那么用sum[i]减它就是一个合法区间.再将当前的s ...
- hdu 1024 最大M子段dp
题目: Max Sum Plus Plus Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Ot ...
- JS水平移动图片
横向: <div id=demo style="overflow:hidden;width:200px;border:2px solid #e0e0e0;padding:2px;&qu ...
- dp练习(7)—— 最小和
3415 最小和 CodeVS原创 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 青铜 Bronze 题解 题目描述 Description 小浣熊松松来到文具店, ...