奇怪的现象
今日在工作的过程中,我用接口文档给的数据,json格式,通过AndroidStudio的GsonFormatPlus插件生成了一个JavaBean类/或者称之为实体类。然后我把bean类中的注释部分删除掉,并生成了对应的get、set方法。json数据格式是这样的:
{
"code":200,
"data":
{"ranking":[
{"score":94,"user":"wys***584@163.com","is_myscore":true},
{"score ":50,"user":"tes***5@xgimi.com","is_myscore":false},
{"score":20,"user":"tes***3@xgimi.com", "is_myscore":false}],
"myrank":1,
"myscore":94},
"message":"ok"
}
Bean类是这样的:
public class RankingBean {
@SerializedName("code")
private int code;
@SerializedName("data")
private DataDTO data;
@SerializedName("message")
private String message;
public static class DataDTO {
@SerializedName("ranking")
private List<RankingDTO> ranking;
@SerializedName("myrank")
private int myrank;
@SerializedName("myscore")
private int myscore;
public static class RankingDTO {
@SerializedName("score")
private int score;
@SerializedName("user")
private String user;
@SerializedName("is_myscore")
private Boolean isMyscore;
}
}
}
我手贱。把上面的@SerializedName("xxxx")删除掉,然后加上了get 和 set方法。变成了如下:
public class RankingBean {
private int code;
private DataDTO data;
private String message;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public DataDTO getData() {
return data;
}
public void setData(DataDTO data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "RankingBean{" +
"code=" + code +
", data=" + data +
", message='" + message + '\'' +
'}';
}
public static class DataDTO {
private List<RankingDTO> ranking;
private int myrank;
private int myscore;
public List<RankingDTO> getRanking() {
return ranking;
}
public void setRanking(List<RankingDTO> ranking) {
this.ranking = ranking;
}
public int getMyrank() {
return myrank;
}
public void setMyrank(int myrank) {
this.myrank = myrank;
}
public int getMyscore() {
return myscore;
}
public void setMyscore(int myscore) {
this.myscore = myscore;
}
@Override
public String toString() {
return "DataDTO{" +
"ranking=" + ranking +
", myrank=" + myrank +
", myscore=" + myscore +
'}';
}
public static class RankingDTO {
private int score;
private String user;
private Boolean isMyscore;
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public Boolean getMyscore() {
return isMyscore;
}
public void setMyscore(Boolean myscore) {
isMyscore = myscore;
}
@Override
public String toString() {
return "RankingDTO{" +
"score=" + score +
", user='" + user + '\'' +
", isMyscore=" + isMyscore +
'}';
}
}
}
}
然后,我用Gson和FastJson写了个测试代码来测试这个类的正确性。代码如下:
String s="{\"code\":200,\"data\":{\"ranking\":[{\"score\":94,\"user\":\"wys***584@163.com\"," +"\"is_myscore\":true},{\"score \":50,\"user\":\"tes***5@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":40,\"user\":\"tes***7@xgimi.com\", \"is_myscore\":false}," +
"{\"score\":35,\"user\":\"tes***8@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":30,\"user\":\"tes ***6@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":28,\"user\":\"tes***2@xgimi.com\",\"is_myscore\":false}," +
"{\"score \":22,\"user\":\"tes***4@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":20,\"user\":\"tes***3@xgimi.com\", \"is_myscore\":false}," +
"{\"score\":10,\"user\":\"tes***1@xgimi.com\",\"is_myscore\":false}]," +
"\"myrank\":1,\"myscore\":94},\"message\":\"ok\"}";
Gson gson=new Gson();
RankingBean bean1=gson.fromJson(s,RankingBean.class);
Log.i("TAG",bean1.toString());
RankingBean bean=JSON.parseObject(s,RankingBean.class);
Log.i("TAG",bean.toString());
结果日志输出如下:
RankingBean{code=200, data=null, message='ok'}
RankingBean{code=200, data=null, message='ok'}
WTF??? data怎么会是null呢。然后问ChatGPT。ChatGPT说得云里来雾里去。然它帮我改一下代码使他能够正常解析data部分的数据。它就是不给我直接改。后面在我各种折磨下,它帮我重新生成了一个JavaBean。大概和我的一样。就是改了RankingDTO的类名。结果替换上去。还是一样的日志输出。
再三追问,和chatGPT磨了一个小时。看来靠ChatGPT没辙了,(谁说ChatGPT能替换程序员的?看你以后的程序能不能正常运行。)就跑到QQ群问广大的程序员朋友。有的朋友说,需要把内部类分开写,写成三个JavaBean文件。有的说要把JavaBean里面的内部类的static去掉。这些都无济于事。依然是向我展示....data=null.... 的结果。我把代码截图,代码发出去。也没人帮我执行一下。即使我说,谁能帮我找出问题所在。我给红包,也没有人愿意试试运行。就在这个百思不得其解的时候。有个网友说,要不你手动实例化,搞一个对象出来。然后通过GSON把他转成字符串,对比一下我写的字符串。我觉得这是一个好主意。但是此时,又有更多人给我更多的建议,比如debug模式运行,下载Gson库,逐步调试。可能是激励的讨论,引来了更多人的围观,有个人说数组不是这样解析的要用Gson的TypeToken。这个设计了我的知识盲区,我说是吗?但是我之前做那么多项目,没用过也没听过要什么TypeToken呀。于是我百度了一下。发现TypeToken的方式似乎不太符合我的方式(这里保留,可能是我没怎么用,不太了解这个)。结果那个人跳出来说,做好了没有,我说,没有,还在看博文。然后它说你是不是不想给红包。我说小小的红包是给得起的。问题是我的问题还没得到解决呀。后来又争论了一番。还是无解。就在一个上午即将过去的时候。我没办法了,又想起了前面别人说的“手动实例化,搞一个对象出来。然后通过GSON把他转成字符串,对比一下我写的字符串”。于是我把测试代码改成了下面这样。
String s="{\"code\":200,\"data\":{\"ranking\":[{\"score\":94,\"user\":\"wys***584@163.com\"," +
"\"is_myscore\":true},{\"score \":50,\"user\":\"tes***5@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":40,\"user\":\"tes***7@xgimi.com\", \"is_myscore\":false}," +
"{\"score\":35,\"user\":\"tes***8@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":30,\"user\":\"tes ***6@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":28,\"user\":\"tes***2@xgimi.com\",\"is_myscore\":false}," +
"{\"score \":22,\"user\":\"tes***4@xgimi.com\",\"is_myscore\":false}," +
"{\"score\":20,\"user\":\"tes***3@xgimi.com\", \"is_myscore\":false}," +
"{\"score\":10,\"user\":\"tes***1@xgimi.com\",\"is_myscore\":false}]," +
"\"myrank\":1,\"myscore\":94},\"message\":\"ok\"}";
Gson gson=new Gson();
RankingBean bean1=gson.fromJson(s,RankingBean.class);
Log.i("TAG",bean1.toString());
RankingBean bean=JSON.parseObject(s,RankingBean.class);
Log.i("TAG",bean.toString());
RankingBean rankingBean=new RankingBean();
rankingBean.setCode(200);
rankingBean.setMessage("ok");
RankingBean.DataDTO dataDTO=new RankingBean.DataDTO();
List<RankingBean.DataDTO.RankingDTO> list=new ArrayList<>();
RankingBean.DataDTO.RankingDTO rankingItem=new RankingBean.DataDTO.RankingDTO();
rankingItem.setMyscore(false);
rankingItem.setUser("tes***4@xgimi.com");
rankingItem.setScore(10);
list.add(rankingItem);
dataDTO.setRanking(list);
rankingBean.setData(dataDTO);
Log.i("TAG",gson.toJson(rankingBean));
这个时候见鬼了。竟然前面data=null的情况消失了。三个Log都能正常输出data。日志如下:
RankingBean{code=200, data=DataDTO{ranking=[RankingDTO{score=94, user='wys***584@163.com', isMyscore=null}, RankingDTO{score=0, user='tes***5@xgimi.com', isMyscore=null}, RankingDTO{score=40, user='tes***7@xgimi.com', isMyscore=null}, RankingDTO{score=35, user='tes***8@xgimi.com', isMyscore=null}, RankingDTO{score=30, user='tes ***6@xgimi.com', isMyscore=null}, RankingDTO{score=28, user='tes***2@xgimi.com', isMyscore=null}, RankingDTO{score=0, user='tes***4@xgimi.com', isMyscore=null}, RankingDTO{score=20, user='tes***3@xgimi.com', isMyscore=null}, RankingDTO{score=10, user='tes***1@xgimi.com', isMyscore=null}], myrank=1, myscore=94}, message='ok'}
RankingBean{code=200, data=DataDTO{ranking=[RankingDTO{score=94, user='wys***584@163.com', isMyscore=true}, RankingDTO{score=0, user='tes***5@xgimi.com', isMyscore=false}, RankingDTO{score=40, user='tes***7@xgimi.com', isMyscore=false}, RankingDTO{score=35, user='tes***8@xgimi.com', isMyscore=false}, RankingDTO{score=30, user='tes ***6@xgimi.com', isMyscore=false}, RankingDTO{score=28, user='tes***2@xgimi.com', isMyscore=false}, RankingDTO{score=0, user='tes***4@xgimi.com', isMyscore=false}, RankingDTO{score=20, user='tes***3@xgimi.com', isMyscore=false}, RankingDTO{score=10, user='tes***1@xgimi.com', isMyscore=false}], myrank=1, myscore=94}, message='ok'}
{"code":200,"data":{"myrank":0,"myscore":0,"ranking":[{"isMyscore":false,"score":10,"user":"tes***4@xgimi.com"}]},"message":"ok"}
为什么会这样。我把这个结果告诉了群友。群友也是百思不得其解。充分证明了,我最开始的带get和set的JavaBean是没有问题的。问题就出现在Gson解析本身。这个时候,有个真大神跳出来了。说是不是编译时候开了混淆。Gson和FastJson使用了反射,所以不正常。这时候,我突然明白了。赶紧看项目配置。果然是开着编译混淆。我把编译混淆去掉。然后测试代码中手动实例化的内容也去掉。结果,还真的是成功输出了data的值。
这个问题终于得到了解决。但是同时产生了新的疑问。为什么在开着代码混淆的情况下,code / message这些能够正常解析,data就不难够正常解析呢。应该大家都不能解析才对。为什么我手动写了实例化之后,他就能全部解析了呢?难道混淆规则在我做了实例化的时候,看透了我这是一个JavaBean不进行混淆?更多的疑问留给以后在分析。
解决方法:
取消代码混淆。当然请求还是用第三方框架,比如Retrofit之类的好。这样一天的中午就结束了。