本文目錄
-
- 前言
- JSON轉換成Dart對象
- 實踐
- 根據JSON用工具生成實體類
-
- 當JSON類屬性與伺服器傳回屬性不一緻時
前一篇博文已經詳細介紹了Flutter開發中的網絡請求,但其實大多數項目中,傳回HTML内容是不夠的,因為移動端使用的最多的請求是JSON資料,是以我們需要掌握Flutter開發中,JSON解析的知識。JSON(javaScript Object Notation,JS對象簡譜)是一種輕量級的資料交換格式)
假設,我們現在是開發的是一款新聞App,通過通路相關的接口之後,伺服器傳回了這樣一條簡單的JSON資料,如下圖所示:
{"title":"疫情疫苗出世,多闆塊重大利好"}
那麼我們應該如何處理這條資料顯示在界面上呢?相信有過Java開發Android經驗的讀者,肯定知道如何把這段資料還原成一個對象,并且在界面顯示出來。同樣,在Flutter開發中,也可以把這個JSON資料轉換為Dart對象,我們先定義Dart對象News,代碼如下:
class News{
final String title;
News({this.title});
factory News.fromJson(Map<String,dynamic> json){
return News(
title: json['title'],
);
}
}
在dart:convert裡面有一個JSON常量,它是負責處理服務端傳回的JSON資料的,在請求響應回來的時,通過json.decode(response.body)方法調用可以把JSON結果轉換城Map類型或List類型。如果是一個JSON對象,傳回将是一個Map;如果是JSON數組,則會傳回List。
上面代碼之是以把map的value定義成dynamic,是因為不肯定value的類型,畢竟有可能是字元串,有可能是整型,還是用這個自動比對類型最實在。
在JSON解析之後,我們需要把結果通過界面的形式展現給使用者看,是以下面我們直接來實作其功能:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState(news: httpPost());
}
class _MyHomePageState extends State<MyHomePage> {
final Future<News> news;
_MyHomePageState({Key key,this.news});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("HttpClient"),
),
body:Center(
child: FutureBuilder<News>(
future: news,
builder: (context,snapshot){
if(snapshot.hasData){
return Text(snapshot.data.title);
}else if(snapshot.hasError){
return Text("錯誤啦");
}
return CircularProgressIndicator();
},
),
),
);
}
}
Future<News> httpPost() async{
final response=await http.get("http://liyuanjinglyj.com/demo/");
if(response.statusCode==200){
print(utf8.decode(response.bodyBytes));
return News.fromJson(json.decode(response.body));
}else{
throw Exception('請求不到JSon');
}
}
class News{
final String title;
News({this.title});
factory News.fromJson(Map<String,dynamic> json){
return News(
title: json['title'],
);
}
}
代碼很簡單,這裡專門定義了一個JSON的Dart類,用于處理JSON資料,同時用FutureBuilder元件将JSON顯示在螢幕上。顯示效果首圖所示。
上面可以說是靜态生成JSON格式,也就是說手動定義了一個JSON類,但是假如有許多許多JSON格式的資料,那怎麼擷取JSON資料呢?一個一個寫肯定麻煩的很,是以我們需要借助工具去自動生成JSON類,這裡就也需要在pubspec.yaml中導入依賴:
dependencies:
json_annotation: ^2.0.0
dev_dependencies:
build_runner: ^1.0.0
json_serializable: ^2.0.0
然後我們建立一個實體類,格式如下:
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable()
class User{
String name;
String email;
User({this.name,this.email});
factory User.fromJson(Map<String,dynamic> json)=>_$UserFromJson(json);
Map<String,dynamic> toJson=>_$UserToJson(this);
}
這裡會報錯,特别是part ‘user.g.dart’;與最後兩句,都會出現紅色的波浪線提示,但這是正常的不用急,我們可以把上面的代碼看成是模闆,然後我們在工程目錄檔案下輸入如下指令:
flutter packages pub run build_runner build
輸入完這個指令之後,就會生成一個user.g.dart檔案,在user.dart同級目錄之中,代碼如下:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'user.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
User _$UserFromJson(Map<String, dynamic> json) {
return User(name: json['name'] as String, email: json['email'] as String);
}
Map<String, dynamic> _$UserToJson(User instance) =>
<String, dynamic>{'name': instance.name, 'email': instance.email};
部落客為了直覺,是以減少了許多參數,但在實際情況中,JSON的參數肯定會有很多,甚至還有層級,那麼這種情況下,這樣自動生成起來肯定比較友善,也比較節約時間。

但這種方式也會有一個缺陷,就是假如我的JSON格式變更了,那還不得一次一次生成?很顯然,Flutter也考慮到這種情況,是以提供給我們一種監聽模式來實作每一次的生成,指令如下:
flutter packages pub run build_runner watch
@JsonKey(name:'user_name')
final String userName;