天天看點

Flutter(二十)——JSON解析

本文目錄

    • 前言
    • 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的參數肯定會有很多,甚至還有層級,那麼這種情況下,這樣自動生成起來肯定比較友善,也比較節約時間。

Flutter(二十)——JSON解析

但這種方式也會有一個缺陷,就是假如我的JSON格式變更了,那還不得一次一次生成?很顯然,Flutter也考慮到這種情況,是以提供給我們一種監聽模式來實作每一次的生成,指令如下:

flutter packages pub run build_runner watch
           

@JsonKey(name:'user_name')
final String userName;