openplanning

Dart JSON với thư viện dart:convert

  1. dart:convert
  2. jsonDecode(..)
  3. jsonDecode(..). JSON --> Dart
  4. jsonEncode(..)
  5. Bài tập thực hành

1. dart:convert

Trong nhiều ứng dụng Dart/Flutter, bạn khá thường xuyên phải làm việc với các dữ liệu có định dạng JSON. Trong bài viết này chúng ta sẽ tìm hiểu cách sử dụng thư viện dart:convert để phân tích một cách thủ công các dữ liệu JSON. Đây là một thư viện tích hợp sẵn trong Dart và phù hợp với các dự án nhỏ.
Đối với các ứng dụng có nhiều mô hình JSON phức tạp hơn, việc sử dụng dart:convert có thể nhanh chóng trở lên tẻ nhạt, lặp đi lặp lại và dẫn đến nhiều lỗi nhỏ. Trong trường hợp đó bạn nên sử dụng thư viện dart_json_mapper hoặc json_serializable.
  • Dart json serializable
Mặc dù hiện tại có nhiều thư viện Dart JSON được cung cấp bởi cộng đồng, tuy nhiên nó không phong phú so với Java. Có thể bạn đặt ra câu hỏi "Có thư viện nào tương đương với GSON/Jackson/Moshi của Java không?", câu trả lời là có, đó là dart_json_mapper.
Là một thư viện đơn giản, dart:convert không cần bất kỳ một cấu hình bổ xung nào và là thư viện xử lý JSON đầu tiên mà bạn cần nghĩ tới, dành cho các dự án nhỏ.
Một vài hàm được cung cấp bởi thư viện dart:convert:
// JSON ==> Dart Object.
dynamic jsonDecode(
  String source, {
  Object? reviver(Object? key, Object? value)?,
}) 

// Dart Object ==> JSON
String jsonEncode(
  Object? object, {
  Object? toEncodable(Object? nonEncodable)?,
}) 

2. jsonDecode(..)

dynamic jsonDecode(
  String source, {
  Object? reviver(Object? key, Object? value)?,
}) 
Hàm jsonDecode() được sử dụng để phân tích (parse) một văn bản JSON và trả về một đối tượng Dart. Hàm này trả về kiểu dynamic. Kiểu trả về này có thể là:
  • Map<String,dynamic>
  • List<dynamic>
  • Một đối tượng Dart nếu tham số reviver khác null (Có thể; xem một ví dụ bên dưới).
Các tham số:
  • source: Một văn bản có định dạng JSON.
  • reviver: Một hàm tuỳ chọn được gọi cho mỗi property tìm thấy trong quá trình phân tích văn bản JSON này. Tham số [key=null] tương ứng với đối tượng JSON toàn cục. Các property ở mức lá sẽ được thực thi đầu tiên, property ở mức gốc ở được thực thi cuối cùng.
Tình huống hàm jsonDecode(json) trả về Map<String,dynamic>:
Tình huống hàm jsonDecode(json) trả về List<dynamic>:
var json1 = '''{
      "name": "John Smith",
      "email": "john@example.com",
      "salary": 5000,
      "addresses": ["Address 1", "Address 2"]
    }''';

Map<String, dynamic> map = jsonDecode(json1);
var json2 = '''
  [
    {"name": "Tom", "country": "American"},
    {"name": "Jerry", "country": "American"} 
  ] 
''';

List<dynamic> list = jsonDecode(json2);
Ví dụ: Hàm jsonDecode() trả về một Map<String,dynamic>:
json_jsonDecode_ex1.dart
import 'dart:convert';

void main() {
  var jsonString = '''{
        "name": "John Smith",
        "email": "john@example.com",
        "salary": 5000,
        "addresses": ["Address 1", "Address 2"]
      }''';  
  Map<String, dynamic> map = jsonDecode(jsonString);

  for (var key in map.keys) {
    print('$key --> ${map[key]}');
  }
  print(' --- ');
 
  List<dynamic> addresses =  map['addresses'];
  for(String address in addresses) {
     print('Address: $address');
  }
}
Output:
name --> John Smith
email --> john@example.com
salary --> 5000
addresses --> [Address 1, Address 2]
 ---
Address: Address 1
Address: Address 2
Ví dụ tiếp theo với JSON phức tạp hơn:
json_jsonDecode_ex2.dart
import 'dart:convert';

void main() {
  var jsonString = '''{
        "name": "John Smith",
        "email": "john@example.com",
        "children": [ 
           {
             "name": "Jack Smith",
             "email": "jack@example.com"
           }, 
           {
             "name": "Harry Smith",
             "email": "harry@example.com"
           }
         ]
      }''';
  Map<String, dynamic> map = jsonDecode(jsonString);

  for (var key in map.keys) {
    print('$key --> ${map[key]}');
  }
  print(' --- ');

  List<dynamic> children = map['children'];
  for (dynamic child in children) {
    var childMap = child as Map<String, dynamic>;
    for (var k in childMap.keys) {
      print('  $k --> ${childMap[k]}');
    }
  }
}
Output:
name --> John Smith
email --> john@example.com
children --> [{name: Jack Smith, email: jack@example.com}, {name: Harry Smith, email: harry@example.com}]
 ---
  name --> Jack Smith
  email --> jack@example.com
  name --> Harry Smith
  email --> harry@example.com
Ví dụ: Hàm jsonDecode() trả về một List<dynamic>:
json_jsonDecode_ex3.dart
import 'dart:convert';

void main() {
  var jsonString = '''[ 
      {
        "name": "Jack Smith",
        "email": "jack@example.com"
      }, 
      {
        "name": "Harry Smith",
        "email": "harry@example.com"
      }
  ]''';
  List<dynamic> list = jsonDecode(jsonString);

  for (var element in list) {
    var map = element as Map<String, dynamic>;
    for (var k in map.keys) {
      print('$k --> ${map[k]}');
    }
  }
}
Output:
name --> Jack Smith
email --> jack@example.com
name --> Harry Smith
email --> harry@example.com

3. jsonDecode(..). JSON --> Dart

Ví dụ chuyển đổi một JSON thành một đối tượng Dart.
json_to_dart_ex1.dart
import 'dart:convert';

void main() {
  var jsonString = '''{
        "name": "John Smith",
        "email": "john@example.com",
        "contact": {
           "address": "Address 1",
           "phone": "12345"
        }  
      }''';

  Employee emp = Employee.fromJsonString(jsonString);

  print('Emp Name: ${emp.name}');
  print('Emp Email: ${emp.email}');
  print('Address: ${emp.contact.address}');
}

class Employee {
  String name;
  String email;
  Contact contact;

  // Constructor
  Employee(this.name, this.email, this.contact);

  static Employee fromMap(Map<String, dynamic> map) {
    String name = map['name'];
    String email = map['email'];
    Map<String, dynamic> contactMap = map['contact'];
    Contact contact = Contact.fromMap(contactMap);

    return Employee(name, email, contact);
  }

  static Employee fromJsonString(String jsonString) {
    Map<String, dynamic> map = jsonDecode(jsonString);
    return fromMap(map);
  }
}

class Contact {
  String address;
  String phone;

  // Constructor
  Contact(this.address, this.phone);

  static Contact fromMap(Map<String, dynamic> map) {
    String address = map['address'];
    String phone = map['phone'];
    return Contact(address, phone);
  }

  static Contact fromJsonString(String jsonString) {
    Map<String, dynamic> map = jsonDecode(jsonString);
    return fromMap(map);
  }
}
Output:
Emp Name: John Smith
Emp Email: john@example.com
Address: Address 1
Example 2: JSON --> Dart
json_to_dart_ex2.dart
import 'dart:convert';

void main() {
  // Department and Staffs
  var jsonString = '''{
        "deptNo" : "D001",
        "deptName": "Sales", 
        "staffs": [
           { "name": "Tom", "phone": "111222"}, 
           { "name": "Jerry", "phone": "222333"}, 
           { "name": "Donald", "phone": "333444"} 
        ]
      }''';

  Department dept = Department.fromJsonString(jsonString);

  print('Dept No: ${dept.deptNo}');
  print('Dept Name: ${dept.deptName}');
  print('Staff Count: ${dept.staffs.length}');

  Staff firstStaff = dept.staffs[0];
  print("First Staff Name: ${firstStaff.name}");
}

class Department {
  String deptNo;
  String deptName;
  List<Staff> staffs;

  Department(this.deptNo, this.deptName, this.staffs);

  static Department fromMap(Map<String, dynamic> map) {
    String deptNo = map['deptNo'];
    String deptName = map['deptName'];
    List<dynamic> list = map["staffs"];
    List<Staff> staffs = list.map((dynamic e) => Staff.fromMap(e)).toList();
    return Department(deptNo, deptName, staffs);
  }

  static Department fromJsonString(String jsonString) {
    Map<String, dynamic> map = jsonDecode(jsonString);
    return fromMap(map);
  }
}

class Staff {
  String name;
  String phone;
  Staff(this.name, this.phone);

  static Staff fromMap(Map<String, dynamic> map) {
    String name = map['name'];
    String phone = map['phone'];
    return Staff(name, phone);
  }
}
Output:
Dept No: D001
Dept Name: Sales
Staff Count: 3
First Staff Name: Tom

4. jsonEncode(..)

String jsonEncode(
  Object? object, {
  Object? toEncodable(Object? nonEncodable)?,
})
Hàm jsonEncode() được sử dụng để chuyển đổi một đối tượng Dart thành một văn bản JSON.
Các tham số:
  • object: Một đối tượng Dart.
  • toEncodable: Một hàm tuỳ chọn được sử dụng để chuyển đổi các đối tượng Dart không thể mã hoá (Non Encodable) thành các đối tượng có thể mã hoá (Encodable). Nếu tham số này không được cung cấp, hàm toJson() sẽ được sử dụng như một sự thay thế.
Ví dụ: Một đối tượng Dart với tất cả các property có thể mã hoá (Encodable):
// An Encodable object:
var employee = {
  'name': 'John Smith',
  'email': 'john@example.com',
  'contact': {
    'address': 'Address 1',
    'phone': '12345'
   }
};
Ví dụ: Một đối tượng Dart với một vài property có thể mã hoá, và một property không thể mã hoá:
var employee = {
  'name': 'John Smith', // Encodable
  'email': 'john@example.com', // Encodable
  'contact': Contact('Address 1', '12345') // Unencodable!!!
};
Ví dụ: Sử dụng hàm jsonEncode() để chuyển một đối tượng Dart thành văn bản JSON:
json_jsonEncode_ex1.dart
import 'dart:convert';

void main() {
  // An Object (Looks like a JSON):
  var employee = {
    'name': 'John Smith',
    'email': 'john@example.com',
    'contact': {
      'address': 'Address 1',
      'phone': '12345'
     }
  };
  var jsonString = jsonEncode(employee);
  print(jsonString);
}
Output:
{"name":"John Smith","email":"john@example.com","contact":{"address":"Address 1","phone":"12345"}}
Ví dụ: Chuyển đổi một đối tượng Map thành văn bản JSON:
json_jsonEncode_ex2.dart
import 'dart:convert';

void main() {
  var map = Map<String,dynamic>();
    
  map['name'] = 'John Smith';
  map['email'] = 'john@example.com';

  var jsonString = jsonEncode(map);
  print(jsonString);
}
Output:
{"name":"John Smith","email":"john@example.com"}
Ví dụ: Sử dụng hàm jsonEncode() với tham số toEncodable:
json_jsonEncode_toEncodable_ex1.dart
import 'dart:convert';
void main() {
  var contact = Contact('Address 1', '12345');
  var employee = {
    'name': 'John Smith', // Encodable
    'email': 'john@example.com', // Encodable
    'contact': contact // Un-encodable!!!
  };
  var jsonString = jsonEncode(employee, toEncodable: (nonEncodable) {
    if (nonEncodable is Contact) {
      return {
        'address': nonEncodable.address,
        'phone': nonEncodable.phone
      };
    }
    return null;
  });
  print(jsonString);
}

class Contact {
  String address;
  String phone;
  Contact(this.address, this.phone); // Constructor
}
Output:
{"name":"John Smith","email":"john@example.com","contact":{"address":"Address 1","phone":"12345"}}
Ví dụ: Sử dụng hàm jsonEncode() với tham số toEncodable:
json_jsonEncode_toEncodable_ex2.dart
import 'dart:convert';

void main() {
  var contact = Contact('Address 1', '12345');
  var employee = Employee('John Smith', 'john@example.com', contact);

  var jsonString = jsonEncode(employee, toEncodable: (nonEncodable) {
    if (nonEncodable is Contact) {
      return {
        'address': nonEncodable.address,
        'phone': nonEncodable.phone
      };
    } else if(nonEncodable is Employee) {
       return {
           'name': nonEncodable.name,
           'email': nonEncodable.email,
           'contact': nonEncodable.contact
       };
    }
  });
  print(jsonString);
}

class Contact {
  String address;
  String phone;
  Contact(this.address, this.phone); // Constructor
}

class Employee {
  String name;
  String email;
  Contact contact;
  Employee(this.name, this.email, this.contact); // Constructor
}