Dart 本身的 dart:convert,就有 jsonEncode 可以將 Object 轉換為 JSON,以及 jsonDecode 能將 JSON 轉換為 Map<String, dynamic>。
jsonEncode 的第一個參數可接受數字、布林、字串、null、List 或 Map,例如,底下將 Map<String, dynamic> 轉換為 JSON:
var map = {
'name' : 'Justin',
'age' : 40,
'childs' : [
{
'name' : 'hamimi',
'age' : 3
}
]
};
var json = jsonEncode(map);
print(json); // 顯示 {"name":"Justin","age":40,"childs":[{"name":"hamimi","age":3}]}
因為 Dart 的 Map、List 的字面表示方式,組合起來很像是 JSON 格式,透過以上方式來轉換 JSON 是蠻常見的做法,第一個參數也可以接受具有 toJson 方法的物件,由物件自身負責 JSON 轉換,例如若有個 User 類別如下:
class User {
String name;
int age;
User({this.name, this.age});
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age
};
}
}
就可以直接將 User 實例餵給 jsonEncode:
var json = jsonEncode(User(name: 'Justin', age: '40'));
print(json); // 顯示 {"name":"Justin","age":"40"}
如果類別沒有定義 toJson,可以在呼叫 jsonEncode 時指定 toEncodable,例如,假設方才的 User 類別沒有定義 toJson:
var json = jsonEncode(User(name: 'Justin', age: '40'), toEncodable: (nonEncodable) {
User user = nonEncodable;
return {
'name': user.name,
'age': user.age
};
});
print(json); // 顯示 {"name":"Justin","age":"40"}
jsonDecode 能將 JSON 轉換為 Map<String, dynamic>,例如:
var json = '''{
"name": "Justin",
"age": 40,
"childs": [
{
"age": 8,
"name": "Irene"
}
]
}''';
Map<String, dynamic> user = jsonDecode(json);
user.forEach((key, value) => print('${key}: ${value}'));
childs 的型態,也就是 user['childs'] 的型態會是 List<Map<String, dynamic>>。jsonDecode 可以指定 reviver 函式,這會在每個被剖析出來的物件上呼叫,reviver 函式的參數是 key 與 value,key 會是字串、索引(List 的情況)或者是 null(整個剖析後的 Map<String, dynamic>),value 是對應的剖析結果物件。
預設的 reviver 函式,單純地傳回 value,你可以在 reviver 調整剖析後的結果,例如將字串轉大寫:
var json = '''{
"name": "Justin",
"age": 40,
"childs": [
{
"age": 8,
"name": "Irene"
}
]
}''';
Map<String, dynamic> user = jsonDecode(json, reviver: (key, value) {
if(value is String) {
return (value as String).toUpperCase();
}
return value;
});
// 字串會是大寫結果
user.forEach((key, value) => print('${key}: ${value}'));
在一些文件上,常見使用以下的模式,在物件模型與 Map<String, dynamic> 之間轉換:
class User {
String name;
int age;
User({this.name, this.age});
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
age = json['age'];
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age
};
}
}
這麼一來,就可以如下搭配 jsonEncode 與 jsonDecode:
var userMap = jsonEncode(User(name: 'Justin', age: 40));
var user = User.fromJson(jsonDecode(userMap));
只不過我個人感覺這有點奇怪,User.fromJson 其實應該命名為 User.fromMap,而 toJson 也應該命名為 toMap,雖然合理的解釋是「為了配合 jsonEncode、jsonDecode 的 API 協定」,不過,比較好的方式,應該是物件模型與 JSON 之間的轉換,把 jsonEncode、jsonDecode 封裝起來吧!
class User {
String name;
int age;
User({this.name, this.age});
User.fromJson(String json) {
var decoded = jsonDecode(json);
name = decoded['name'];
age = decoded['age'];
}
String json() {
return jsonEncode({
'name': name,
'age': age
});
}
}
如此一來,就可以如下直接轉換:
var json = User(name: 'Justin', age: 40).json();
var user = User.fromJson(json);

