一、啥是序列化

在计算机的世界里,序列化就像是给数据穿上一件能在不同地方“旅行”的外套。简单来说,序列化就是把对象变成一种可以存储或者传输的格式,反序列化呢,就是把这个格式再变回对象。在 Dart 里,最常见的序列化格式就是 JSON 了。为啥选 JSON 呢?因为它简单、通用,好多编程语言都支持。

比如说,你有一个 Dart 对象,里面有一些属性,像名字、年龄啥的。如果你想把这个对象存到文件里,或者通过网络发给别人,就需要把它变成 JSON 格式。等对方收到后,再把 JSON 变回对象。这一过程就是序列化和反序列化。

二、Dart 里的 JSON 转换

简单对象的 JSON 转换

在 Dart 里,要做 JSON 转换,得用到 dart:convert 这个库。咱先看个简单的例子:

// Dart 技术栈
import 'dart:convert';

// 定义一个简单的类
class Person {
  String name;
  int age;

  // 构造函数
  Person(this.name, this.age);

  // 将对象转换为 Map
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'age': age,
    };
  }

  // 从 Map 创建对象
  factory Person.fromJson(Map<String, dynamic> json) {
    return Person(
      json['name'],
      json['age'],
    );
  }
}

void main() {
  // 创建一个 Person 对象
  Person person = Person('张三', 25);

  // 将 Person 对象转换为 JSON 字符串
  String jsonString = json.encode(person.toJson());
  print('JSON 字符串: $jsonString');

  // 将 JSON 字符串转换回 Person 对象
  Map<String, dynamic> jsonMap = json.decode(jsonString);
  Person newPerson = Person.fromJson(jsonMap);
  print('新的 Person 对象: ${newPerson.name}, ${newPerson.age}');
}

在这个例子里,我们定义了一个 Person 类,有 nameage 两个属性。toJson 方法把对象变成一个 MapfromJson 方法从 Map 创建对象。然后用 json.encodeMap 变成 JSON 字符串,用 json.decode 把 JSON 字符串变回 Map,最后再用 fromJson 方法把 Map 变成 Person 对象。

嵌套对象的 JSON 转换

有时候,对象里还会包含其他对象,这就是嵌套对象。看下面的例子:

// Dart 技术栈
import 'dart:convert';

// 定义一个 Address 类
class Address {
  String city;
  String street;

  // 构造函数
  Address(this.city, this.street);

  // 将对象转换为 Map
  Map<String, dynamic> toJson() {
    return {
      'city': city,
      'street': street,
    };
  }

  // 从 Map 创建对象
  factory Address.fromJson(Map<String, dynamic> json) {
    return Address(
      json['city'],
      json['street'],
    );
  }
}

// 定义一个 Person 类,包含 Address 对象
class Person {
  String name;
  int age;
  Address address;

  // 构造函数
  Person(this.name, this.age, this.address);

  // 将对象转换为 Map
  Map<String, dynamic> toJson() {
    return {
      'name': name,
      'age': age,
      'address': address.toJson(),
    };
  }

  // 从 Map 创建对象
  factory Person.fromJson(Map<String, dynamic> json) {
    return Person(
      json['name'],
      json['age'],
      Address.fromJson(json['address']),
    );
  }
}

void main() {
  // 创建一个 Address 对象
  Address address = Address('北京', '长安街');
  // 创建一个 Person 对象
  Person person = Person('李四', 30, address);

  // 将 Person 对象转换为 JSON 字符串
  String jsonString = json.encode(person.toJson());
  print('JSON 字符串: $jsonString');

  // 将 JSON 字符串转换回 Person 对象
  Map<String, dynamic> jsonMap = json.decode(jsonString);
  Person newPerson = Person.fromJson(jsonMap);
  print('新的 Person 对象: ${newPerson.name}, ${newPerson.age}, ${newPerson.address.city}, ${newPerson.address.street}');
}

在这个例子里,Person 类里有一个 Address 对象。toJson 方法把 Address 对象也变成 Map 放到 PersonMap 里,fromJson 方法从 Map 里取出 AddressMap,再用 Address.fromJson 方法创建 Address 对象。

三、自定义编码解码器

有时候,JSON 转换的默认规则不能满足我们的需求,这时候就需要自定义编码解码器了。

自定义编码器

自定义编码器就是自己定义把对象变成 JSON 字符串的规则。看下面的例子:

// Dart 技术栈
import 'dart:convert';

// 定义一个简单的类
class Person {
  String name;
  int age;

  // 构造函数
  Person(this.name, this.age);
}

// 自定义编码器
class PersonEncoder extends JsonEncoder {
  @override
  String convert(Object? object) {
    if (object is Person) {
      return '{"name": "${object.name}", "age": ${object.age}}';
    }
    return super.convert(object);
  }
}

void main() {
  // 创建一个 Person 对象
  Person person = Person('王五', 35);

  // 使用自定义编码器将 Person 对象转换为 JSON 字符串
  PersonEncoder encoder = PersonEncoder();
  String jsonString = encoder.convert(person);
  print('自定义编码后的 JSON 字符串: $jsonString');
}

在这个例子里,我们定义了一个 PersonEncoder 类,继承自 JsonEncoder。重写了 convert 方法,当对象是 Person 类型时,按照我们自己的规则把它变成 JSON 字符串。

自定义解码器

自定义解码器就是自己定义把 JSON 字符串变回对象的规则。看下面的例子:

// Dart 技术栈
import 'dart:convert';

// 定义一个简单的类
class Person {
  String name;
  int age;

  // 构造函数
  Person(this.name, this.age);
}

// 自定义解码器
class PersonDecoder extends JsonDecoder {
  @override
  Object? convert(String input) {
    Map<String, dynamic> jsonMap = json.decode(input);
    return Person(jsonMap['name'], jsonMap['age']);
  }
}

void main() {
  // 定义一个 JSON 字符串
  String jsonString = '{"name": "赵六", "age": 40}';

  // 使用自定义解码器将 JSON 字符串转换为 Person 对象
  PersonDecoder decoder = PersonDecoder();
  Person person = decoder.convert(jsonString) as Person;
  print('自定义解码后的 Person 对象: ${person.name}, ${person.age}');
}

在这个例子里,我们定义了一个 PersonDecoder 类,继承自 JsonDecoder。重写了 convert 方法,把 JSON 字符串变成 Map,再用 Map 创建 Person 对象。

四、应用场景

数据存储

当你想把对象存到文件或者数据库里时,就需要把对象序列化。比如,你有一个用户信息的对象,要把它存到本地文件里,就可以把它变成 JSON 字符串再存。等需要用的时候,再把 JSON 字符串反序列化回对象。

网络传输

在网络通信中,数据需要在不同的设备之间传输。这时候就需要把对象序列化,变成可以在网络上传输的格式,比如 JSON。接收方收到后,再把 JSON 反序列化回对象。

缓存

有时候,为了提高性能,会把一些经常使用的数据缓存起来。这时候就可以把对象序列化后存到缓存里,等需要用的时候再反序列化。

五、技术优缺点

优点

  • 简单通用:JSON 格式简单易懂,好多编程语言都支持,方便不同系统之间的数据交换。
  • 可读性强:JSON 字符串是文本格式,人也能很容易看懂,方便调试和维护。
  • 灵活性高:可以自定义编码解码器,满足不同的需求。

缺点

  • 性能问题:序列化和反序列化过程会消耗一定的性能,尤其是处理大量数据的时候。
  • 安全问题:如果 JSON 数据来自不可信的来源,可能会存在安全风险,比如 JSON 注入攻击。

六、注意事项

处理异常

在进行 JSON 转换时,可能会出现各种异常,比如 JSON 格式错误。所以要做好异常处理,避免程序崩溃。

// Dart 技术栈
import 'dart:convert';

void main() {
  String jsonString = '{"name": "孙七", "age": 45'; // 错误的 JSON 格式
  try {
    Map<String, dynamic> jsonMap = json.decode(jsonString);
    print('JSON 解码成功');
  } catch (e) {
    print('JSON 解码失败: $e');
  }
}

数据类型问题

JSON 里只有几种基本的数据类型,比如字符串、数字、布尔值等。在进行序列化和反序列化时,要注意数据类型的转换。

自定义编码解码器的复杂度

自定义编码解码器虽然灵活,但也会增加代码的复杂度。要根据实际需求来决定是否使用。

七、文章总结

Dart 里的序列化和 JSON 转换是很重要的技术,能让我们在不同场景下方便地存储、传输和处理数据。通过 dart:convert 库,我们可以轻松地进行简单对象和嵌套对象的 JSON 转换。当默认的转换规则不能满足需求时,还可以自定义编码解码器。不过,在使用过程中,要注意处理异常、数据类型问题,以及自定义编码解码器的复杂度。