Flutter 中用 Dart 解析 JSON 对象、数组、嵌套对象等数据

项目中绝大部分数据都来自后台接口,而且数据大多是以 JSON 格式返回,前端要展示数据就要解析 JSON 数据,在 Flutter 中如何用 Dart 解析 JSON 数据呢?比如解析 JSON 对象、数组/集合、嵌套对象等等。当然,很多地方也将这个“解析”叫做序列化数据,也就是将 JSON 字符串序列化成对象。

这里的序列化不同于我们常说的“反序列化”,所以这两个序列化很容易混淆,后面我都用解析

JSON 数据格式

大部分后台返回的 JSON 数据格式大概有这三种:

JSON 对象(Object)
{
    "stationId": 3,
    "stationName": "黄埔支行"
}

JSON 数组/集合(List)
[
    {
        "stationId": 3,
        "stationName": "黄埔支行"
    },
    {
        "stationId": 4,
        "stationName": "徐汇支行"
    }
]

{
    "stations": [
        {
            "stationId": 3,
            "stationName": "黄埔支行"
        },
        {
            "stationId": 4,
            "stationName": "徐汇支行"
        }
    ]
}

JSON 对象嵌套
{
    "stationId": 3,
    "stationName": "黄埔支行",
    "address": {
        "country": "中国",
        "province": "上海"
    }
}

解析 JSON 对象字符串

其实这里分成两种情况,有的对象很简单,简单到不用构造对象实体类,这种 JSON 数据就可以直接在代码中解析,不用构造对象。这里的 jsonDecode 是 Flutter 内置 API ,直接使用即可。

    String jsonString = '{"stationId":3,"stationName":"黄埔支行"}';
    Map<String,dynamic> station = jsonDecode(jsonString);
    print(station["stationName"]);
    
    -----
    黄埔支行

但实际项目中,大部分 JSON 对象数据不会这么简单,对象内有很多字段,字段的数据类型也是各种各样,这种 JSON 的解析都是先构造 JSON 对应的实体类,然后再去映射解析。

做 JAVA 或者 Android 开发时,像 IntelliJ 有插件,可以一键将 JSON 字符串生成对应实体类 Model。但 Flutter 中并没这样的插件和工具,因为这样的插件运行生成 Model 时需要用到反射,而反射在 Flutter 是不能用的

一个个字段手动构造对象就没必要,很多人会使用官方依赖包 json_serializable 来构造对象,我觉得不好用,更推荐这个 在线工具

虽然是个在线网站,但确实好用,一键生成的实体类还自动实现了 fromJsontoJson方法,这两个方法都是特别常用的方法。有了实体类,它的解析代码如下:

    String jsonString = '{"stationId":3,"stationName":"黄埔支行"}';
    Station station = Station.fromJson(jsonDecode(jsonString));
    print(station.stationName);
    
    ---
    黄埔支行

解析 JSON 数组字符串

按照我项目碰到的情况,JSON 数组字符串也分成两种情况,一种是数组的每一项元素就是简单的基本类型,另外一种情况,数组每一项元素是对象。

{
    "stationNames": [
        "黄埔支行",
        "徐汇支行",
        "静安支行"
    ]
}
----------
[
    {
        "stationId": 3,
        "stationName": "黄埔支行"
    },
    {
        "stationId": 4,
        "stationName": "徐汇支行"
    }
]

我们先来看数组每一项是基本数据类型的,比如上面数组中都是 String 字符串,它的代码如下

  	String jsonString = '{"stationNames":["黄埔支行","徐汇支行","静安支行"]}';
    List<String> stationNames = List.from(jsonDecode(jsonString)["stationNames"]);
    print(stationNames);
    
    ---
    [黄埔支行, 徐汇支行, 静安支行]

如果是数组每一项是对象,和解析对象一样,先使用 JSON to Dart 工具构造出对象。我使用的 JSON 对象数据和上面一样,这里就不用再生成,直接使用,代码如下:

  String jsonString = '[{"stationId":3,"stationName":"黄埔支行"},{"stationId":4,"stationName":"徐汇支行"}]';
    final List<dynamic> stationsJson = jsonDecode(jsonString);
    List<Station> stationList = List<Station>.from(stationsJson.map((e) => Station.fromJson(e)));
    print(stationList);
    
    ---
    [Station{stationId: 3, stationName: 黄埔支行}, Station{stationId: 4, stationName: 徐汇支行}]

这里有个事要说明,就是 jsonDecode 的用法。它的返回值要根据实际 JSON 数据确定,如果是对象则用 Map 接收,如果是数组,就用 List。

	 Map<String,dynamic> station = jsonDecode(jsonString);
   final List<dynamic> stationsJson = jsonDecode(jsonString);

另外多说一句,上面的解析数组的代码,我们可以放到构造的对象中。

class Station {
  
  ...
  static List<Station> listFromJson(list) =>
      List<Station>.from(list.map((x) => Station.fromJson(x)));
}

避免如果多个地方需要用到,不用重复写代码,想使用直接调用就行。

    List<Station> stationList = Station.listFromJson(stationsJson);

解析嵌套 JSON 对象

{
    "stationId": 3,
    "stationName": "黄埔支行",
    "address": {
        "country": "中国",
        "province": "上海"
    }
}

这应该是项目开发中最常见的 JSON 数据。解析过程还是一样,简单粗暴将整个 JSON 放到工具生成对应实体类。

class Station {
  int? stationId;
  String? stationName;
  Address? address;

  Station({this.stationId, this.stationName, this.address});

  Station.fromJson(Map<String, dynamic> json) {
    stationId = json['stationId'];
    stationName = json['stationName'];
    address =
        json['address'] != null ? new Address.fromJson(json['address']) : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['stationId'] = this.stationId;
    data['stationName'] = this.stationName;
    if (this.address != null) {
      data['address'] = this.address!.toJson();
    }
    return data;
  }
}

class Address {
  String? country;
  String? province;

  Address({this.country, this.province});

  Address.fromJson(Map<String, dynamic> json) {
    country = json['country'];
    province = json['province'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['country'] = this.country;
    data['province'] = this.province;
    return data;
  }
}

记得添加toString方法,解析代码如下

  String jsonString = '{"stationId":3,"stationName":"黄埔支行","address":{"country":"中国","province":"上海"}}';
    Map<String,dynamic> stationsJson = jsonDecode(jsonString);
    Station station = Station.fromJson(stationsJson);
    print(station);
    
    ---
    Station{stationId: 3, stationName: 黄埔支行, address: Address{country: 中国, province: 上海}}

这个过程和解析单个对象 JSON 是一样的,但还有种情况,如果其他对象也包含这里的 Address 对象,也就是被嵌套的对象其他 JSON 也使用到,这样每次都会再生成一个 Address 对象。虽然没啥问题,但还是不好看。

{
    "orderNum": "1223424",
    "orderTitle": "买了个表",
    "address": {
        "country": "中国",
        "province": "上海"
    }
}

对这种数据的解析,最好还是将公共的对象提出来,单独构造一个对象。

再去删掉重复生成的对象,引入公共的对象。OK,Flutter/Dart 的 JSON 解析基本就是这些内容,希望帮到你。

 

本文由老郭种树原创,转载请注明:https://guozh.net/flutter-parses-json-data-with-dart/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注