Android开发:解决一个Json字符串用Gson或者FastJson解析数组为null的问题

开发者在使用Gson和FastJson解析包含嵌套JavaBean的JSON字符串时,发现数据字段data始终为null。经过一系列尝试,包括修改JavaBean结构、使用TypeToken等,问题仍未解决。最终发现是编译时的混淆设置导致的问题,关闭混淆后,解析正常工作。这表明混淆可能影响到了反射机制的正确执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

奇怪的现象

今日在工作的过程中,我用接口文档给的数据,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之类的好。这样一天的中午就结束了。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值