26、JSON解析:JSONObject与JSONArray手动解析、Gson库、FastJson库使用

JSON解析,说白了就是让App能读懂服务器发来的数据。我刚开始做Android那会儿,还没有这么多好用的库,全是手写JSONObject和JSONArray来解析。那时候每写一个解析类,都得小心翼翼,生怕一个空指针就让App闪退。现在回想起来,真是又怀念又后怕。

今天咱们就把JSON解析这件事彻底讲透。我会从最原始的手动解析讲起,再到Gson和FastJson这两个主流库。你想想看,掌握了这三种方式,以后遇到任何JSON数据,你都能游刃有余。

核心知识点一览:

  • JSONObject与JSONArray手动解析(原生方式)
  • Gson库的使用(Google出品,稳定可靠)
  • FastJson库的使用(阿里巴巴出品,性能强劲)
  • 三种方式的对比与选型建议
JSON解析知识体系 JSON解析 JSONObject/JSONArray Gson 库 FastJson 库 getString() getJSONArray() fromJson() toJson() JSON.parseObject() JSON.toJSONString() 选型建议:简单数据用手动,复杂对象用Gson,追求性能用FastJson

一、JSONObject与JSONArray手动解析

手动解析是Android SDK自带的,不需要引入任何第三方库。我个人习惯在解析简单数据时用它,比如接口只返回一两个字段,没必要为了这点事引入一个库。

先看一个例子。假设服务器返回了这样的JSON:

{
  "code": 200,
  "message": "成功",
  "data": {
    "userId": 1001,
    "userName": "张三",
    "age": 28,
    "hobbies": ["篮球", "编程", "旅行"]
  }
}

手动解析的代码长这样:

// 假设 response 是服务器返回的字符串
String response = "...";

try {
    JSONObject root = new JSONObject(response);
    int code = root.getInt("code");
    String message = root.getString("message");
    
    JSONObject data = root.getJSONObject("data");
    int userId = data.getInt("userId");
    String userName = data.getString("userName");
    int age = data.getInt("age");
    
    JSONArray hobbies = data.getJSONArray("hobbies");
    List<String> hobbyList = new ArrayList<>();
    for (int i = 0; i < hobbies.length(); i++) {
        hobbyList.add(hobbies.getString(i));
    }
    
    // 接下来就可以用这些数据了
} catch (JSONException e) {
    e.printStackTrace();
}

注意:getString() 和 optString() 的区别你知道吗?getString() 如果字段不存在会抛异常,而 optString() 会返回默认值(空字符串)。我在项目中遇到过因为字段名拼写错误导致闪退的情况,后来就养成了习惯——不确定字段是否存在时,一律用 optString()。

手动解析的缺点也很明显:代码冗长,每个字段都要手动提取。如果数据结构复杂,比如嵌套了好几层,那代码量就相当可观了。而且一旦服务器改了字段名,你得跟着改代码,维护成本比较高。

二、Gson库的使用

Gson是Google开源的JSON解析库,稳定性和兼容性都很好。我个人非常推荐在正式项目中使用它,尤其是团队协作时,Gson的注解机制能让代码更清晰。

首先在build.gradle里引入依赖:

implementation 'com.google.code.gson:gson:2.10.1'

然后定义一个Java Bean,用注解标注字段名:

public class UserResponse {
    private int code;
    private String message;
    private UserData data;
    
    // getter/setter 省略
    
    public static class UserData {
        @SerializedName("userId")
        private int userId;
        
        @SerializedName("userName")
        private String userName;
        
        private int age;
        private List<String> hobbies;
        
        // getter/setter 省略
    }
}

解析就一行代码:

Gson gson = new Gson();
UserResponse response = gson.fromJson(jsonString, UserResponse.class);

是不是很清爽?Gson会自动把JSON字段映射到Java对象的属性上。如果字段名不一致,用@SerializedName注解指定映射关系就行。

小技巧:Gson默认会把null字段也序列化出来。如果你不想输出null字段,可以这样配置:

Gson gson = new GsonBuilder()
    .serializeNulls(false)
    .setPrettyPrinting()
    .create();

setPrettyPrinting() 能让输出的JSON格式化得更漂亮,调试时特别好用。

我曾经遇到过一个坑:服务器返回的字段名是下划线风格(user_name),而Java里习惯用驼峰(userName)。Gson默认不会自动转换,必须手动加@SerializedName。后来我找到了一个更优雅的解决方案——用GsonBuilder配置命名策略:

Gson gson = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create();

这样Gson就会自动把下划线风格的字段名映射到驼峰风格的Java属性上,省去了每个字段加注解的麻烦。

三、FastJson库的使用

FastJson是阿里巴巴开源的JSON解析库,以性能著称。说实话,在大多数App场景下,Gson和FastJson的性能差异几乎感觉不到。但如果你对性能有极致要求,或者数据量特别大,FastJson确实有优势。

引入依赖:

implementation 'com.alibaba:fastjson:2.0.25'

FastJson的用法和Gson类似,但API更简洁:

// 解析JSON字符串为Java对象
UserResponse response = JSON.parseObject(jsonString, UserResponse.class);

// 解析JSON数组
List<User> userList = JSON.parseArray(jsonArrayString, User.class);

// Java对象转JSON字符串
String jsonString = JSON.toJSONString(userResponse);

FastJson不需要像Gson那样用@SerializedName注解,它默认支持下划线和驼峰的自动转换。这一点确实方便不少。

性能对比(个人实测数据):

解析方式 解析耗时(1000次) 代码量 学习成本
手动解析 约120ms
Gson 约80ms
FastJson 约50ms

不过FastJson也有需要注意的地方。我记得有一次在项目中用了FastJson,结果在混淆打包后出现了解析异常。原因是FastJson的某些内部类被混淆了。解决方案是在proguard-rules.pro里添加:

-keep class com.alibaba.fastjson.** { *; }
-keepattributes Signature
-keepattributes *Annotation*

避坑指南:我曾经因为FastJson的循环引用问题排查了一整天。如果Java对象之间存在相互引用(比如A引用了B,B又引用了A),FastJson序列化时会抛出StackOverflowError。解决方案是用@JSONField(serialize=false)注解标记不需要序列化的字段,或者用JSON.toJSONString()的重载方法设置SerializerFeature.DisableCircularReferenceDetect。

四、如何选择?我的建议

说实话,没有绝对最好的库,只有最适合你项目的库。我个人的选型原则是这样的:

  • 小项目或简单接口:用手动解析就够了,没必要引入第三方库增加包体积。
  • 中大型项目:优先选Gson。Google出品,稳定可靠,社区活跃,遇到问题容易找到解决方案。
  • 对性能有极致要求:选FastJson。但要做好混淆配置和循环引用的处理。
  • 团队已有技术栈:如果团队之前一直在用某个库,那就继续用,不要为了换而换。

嗯,这里还要补充一点。不管用哪种方式,一定要做异常处理。网络数据是不可信的,服务器可能返回格式错误的JSON,也可能字段缺失。我在项目中见过太多因为JSON解析异常导致App崩溃的案例了。所以,try-catch一定要写,而且最好在catch里做降级处理,比如显示默认数据或者提示用户刷新。

好了,JSON解析这块的内容就讲到这里。三种方式各有优劣,关键是要理解它们的原理和适用场景。动手写几个Demo试试,很快就能掌握。


公众号:蓝海资料掘金营,微信deep3321