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);