天天看點

在Flutter中解析複雜的JSONJSON結構#1:簡單 map通路對象JSON結構#2:具有數組的簡單結構Json結構#3:簡單嵌套結構JSON結構#4:具有清單的嵌套結構JSON結構#5: List of mapsJSON結構#6:複雜的嵌套結構FlutterJson數組轉換為List對象及Dio請求結果換為List對象

在Flutter / Dart中使用JSON後,我錯過了Android的gson 世界。當我開始在Flutter中使用API時,JSON解析确實使我非常費力。而且我敢肯定,這會讓很多初學者感到困惑。

我們将使用

dart:convert

此部落格的内置庫。這是最基本的解析方法,僅在從Flutter開始或正在建構小型項目時才建議使用。盡管如此,了解Flutter中JSON解析的基礎還是很重要的。如果您擅長于此,或者需要處理較大的項目,請考慮使用json_serializable等代碼生成器庫。如果可能,我将在以後的文章中找到它們。

Fork this sample project. 它包含您可以嘗試的此部落格的所有代碼。

JSON結構#1:簡單 map

讓我們從student.json中的簡單JSON結構開始

{
  "id":"487349",
  "name":"Pooja Bhaumik",
  "score" : 1000
}

           

規則1: 确定結構。Json字元串将具有一個Map(鍵-值對)或一個Map清單。

規則2:從大括号開始?這是一map.

以方括号開頭?That’s a List of maps.**

student.json

顯然是map. ( E.g like,

id

is a key, and

487349

is the value for

id

)

讓我們為此json結構制作一個PODO(普通的舊式Dart對象?)檔案。您可以在示例項目的student_model.dart中找到此代碼。

class Student{
  String studentId;
  String studentName;
  int studentScores;

  Student({
    this.studentId,
    this.studentName,
    this.studentScores
 });}

           

完美的!

*是嗎 因為json映射與此PODO檔案之間沒有映射。甚至實體名稱都不比對。

*我知道我知道。我們還沒有完成。我們必須完成将這些類成員映射到json對象的工作。為此,我們需要建立一個

factory

方法。根據Dart文檔,我們

factory

在實作構造器時使用了關鍵字,該構造器并不總是建立其類的新執行個體,而這正是我們現在所需要的。

factory Student.fromJson(Map<String, dynamic> parsedJson){
    return Student(
      studentId: parsedJson['id'],
      studentName : parsedJson['name'],
      studentScores : parsedJson ['score']
    );
  }

           

在這裡,我們正在建立一個工廠方法

Student.fromJson

,該方法的目标是簡單地反序列化您的json。

我有點菜鳥,您能告訴我有關反序列化的資訊嗎?

當然。首先讓我們介紹一下序列化和反序列化。序列化隻是意味着将資料(可能在對象中)寫入字元串,而反序列化則相反。它擷取原始資料并重建對象模型。在本文中,我們主要将處理反序列化部分。在第一部分中,我們将從中反序列化json字元串

student.json

So our factory method could be called as our converter method.

還必須注意

fromJson

方法中的參數。這是一個

Map<String, dynamic>

意思是它映射一個

String

鍵和一個

dynamic

值。這正是我們需要确定結構的原因。 If this json structure were a List of maps, 則此參數将有所不同

***但是為什麼要動态?****

*讓我們先看看另一個json結構來回答您的問題

在Flutter中解析複雜的JSONJSON結構#1:簡單 map通路對象JSON結構#2:具有數組的簡單結構Json結構#3:簡單嵌套結構JSON結構#4:具有清單的嵌套結構JSON結構#5: List of mapsJSON結構#6:複雜的嵌套結構FlutterJson數組轉換為List對象及Dio請求結果換為List對象

name

is a Map<String, String> ,

majors

is a Map of String and List and

subjects

is a Map of String and List

由于密鑰始終是

string

并且值可以是任何類型,是以我們将其保持

dynamic

安全。

Check the full code for

student_model.dart

here.

通路對象

讓我們來編寫

student_services.dart

其中的代碼,該代碼可以調用

Student.fromJson

并從

Student

對象中檢索值

Snippet #1 : imports

import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
import 'package:flutter_json/student_model.dart';

           

最後的導入将是模型檔案的名稱

**Snippet #2 : 加載Json Asset(可選)

Future<String> _loadAStudentAsset() async {
  return await rootBundle.loadString('assets/student.json');
}

           

在這個特定的項目中,我們的json檔案位于assets檔案夾中,是以我們必須以這種方式加載json。但是,如果您将json檔案存儲在雲中,則可以進行網絡調用。網絡調用不在本文讨論範圍之内。

Snippet #3 : 加載響應

Future loadStudent() async {
  String jsonString = await _loadAStudentAsset();
  final jsonResponse = json.decode(jsonString);
  Student student = new Student.fromJson(jsonResponse);
  print(student.studentScores);
}

           

在此

loadStudent()

方法中,

Line 1 : 加載原始json字元串。from the assets.

Line 2 : 解碼我們得到的原始json字元串

Line 3 : 現在,我們通過調用

Student.fromJson

方法來反序列化解碼的json響應,以便我們現在可以使用

Student

object通路實體

Line 4 : 就像我們在這裡所做的那樣,where we printed

studentScores

from

Student

class.

檢查Flutter控制台以檢視所有列印值。(In Android Studio, its under Run tab)*

瞧!您隻是進行了第一個JSON解析(或沒有)。

注意:請記住這裡的3個代碼段,我們将使用它來進行下一組json解析(僅更改檔案名和方法名稱),在此我将不再重複代碼。但是您仍然可以在示例項目中找到所有内容。

JSON結構#2:具有數組的簡單結構

現在,我們征服了一種與上述結構相似的json結構,但它可能不僅具有單個值,而且還可能have an array of values.

{
  "city": "Mumbai",
  "streets": [
    "address1",
    "address2"
  ]
}

           

So in this address.json, we have

city

entity that has a simple

String

value, but

streets

is an array of

String

.

據我所知,Dart沒有數組資料類型,但是有 a List 是以這裡

streets

是一個

List<String>

現在我們必須檢查Rule#1和Rule#2。這絕對是一 map 因為它以花括号開頭。.

streets

is still a

List

though, 但我們稍後會擔心。

是以

address_model.dart

最初看起來像這樣

class Address {
  final String city;
  final List<String> streets;

  Address({
    this.city,
    this.streets
  });
}

           

現在,由于這是一amap, 是以我們的

Address.fromJson

方法将仍然具有一個

Map<String, dynamic>

參數。

factory Address.fromJson(Map<String, dynamic> parsedJson) {

  return new Address(
      city: parsedJson['city'],
      streets: parsedJson['streets'],
  );
}

           

現在,

address_services.dart

通過添加我們上面提到的3個片段來建構。必須記住要放置正确的檔案名和方法名。示例項目已經

*address_services.dart*

為您建構。

現在,當您運作此指令時,您将得到一個不錯的小錯誤。:/

type 'List<dynamic>' is not a subtype of type 'List<String>'

           

我告訴你,這些錯誤幾乎都發生在我使用Dart開發的每個步驟中。而且您也将擁有它們。是以,讓我解釋一下這意味着什麼。我們正在請求一個,

List<String>

但是卻收到一個,

List<dynamic>

因為我們的應用程式尚無法識别類型。

是以,我們必須将其明确轉換為

List<String>

var streetsFromJson = parsedJson['streets'];
List<String> streetsList = new List<String>.from(streetsFromJson);

           

在這裡,首先我們将變量映射

streetsFromJson

streets

實體。

streetsFromJson

還是一個

List<dynamic>

。現在,我們明确地建立一個新的

List<String> streetsList

包含所有元素的

streetsFromJson

在此處檢查更新的方法。現在注意return語句。

現在,您可以運作此程式,

*address_services.dart*

它将完美運作。

Json結構#3:簡單嵌套結構

現在,如果我們有一個來自shape.json的嵌套結構,該怎麼辦

{
  "shape_name":"rectangle",
  "property":{
    "width":5.0,
    "breadth":10.0
  }
}

           

在這裡,

property

包含一個對象而不是基本的原始資料類型。

那麼PODO會是什麼樣子?

好吧,讓我們分解一下。In our

shape_model.dart

, let’s make a class for

Property

first.

class Property{
  double width;
  double breadth;

  Property({
    this.width,
    this.breadth
});
}

           

現在,我們為建構類

Shape

。我将兩個類都放在同一個Dart檔案中。

class Shape{
  String shapeName;
  Property property;

  Shape({
    this.shapeName,
    this.property
  });
}

           

請注意,第二個資料成員

property

is basically an object of是我 們上一類的對象

Property

.

規則3:對于嵌套結構,請首先建立類和構造函數,然後從底層添加工廠方法

所謂底層,是指首先征服

*Property*

階級,然後再上一層

*Shape*

*。

By bottom level, we mean, first we conquer

*Property*

class, and then we go one level above to the

*Shape*

class. 這隻是我的建議,而不是Flutter規則。

factory Property.fromJson(Map<String, dynamic> json){
  return Property(
    width: json['width'],
    breadth: json['breadth']
  );
}

           

這是一 simple map.*

但是對于我們 factory method at

Shape

class, 我們不能僅僅這樣做

factory Shape.fromJson(Map<String, dynamic> parsedJson){
  return Shape(
    shapeName: parsedJson['shape_name'],
    property : parsedJson['property']
  );
}

           

property : parsedJson['property']

首先,這将引發類型不比對錯誤-

type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Property'

           

And second, *hey we just *制作了一個不錯的 little class for Property, 我看不到它在任何地方都有用。

Right.我們必須在此處映射我們的Property類。

factory Shape.fromJson(Map<String, dynamic> parsedJson){
  return Shape(
    shapeName: parsedJson['shape_name'],
    property: Property.fromJson(parsedJson['property'])
  );
}

           

是以,基本上,我們

Property.fromJson

Property

類中調用該方法,作為傳回,we map it to the

property

entity. Simple! Check out the code here.

Run this with your

shape_services.dart

and you are good to go.

JSON結構#4:具有清單的嵌套結構

讓我們檢查一下product.json

{
  "id":1,
  "name":"ProductName",
  "images":[
    {
      "id":11,
      "imageName":"xCh-rhy"
    },
    {
      "id":31,
      "imageName":"fjs-eun"
    }
  ]
}

           

好的,現在我們越來越深入。我在内部某處看到對象清單。哇。 Woah.*

是的,是以此結構具有一個對象清單,但其本身仍是 a map. (請參閱規則1和規則2)。現在參考規則3,讓我們構造我們的

product_model.dart

是以我們建立了兩個新類

Product

Image

Note:

*Product*

will 将具有一個資料成員,該成員是 List of

*Image*

class Product {
  final int id;
  final String name;
  final List<Image> images;

  Product({this.id, this.name, this.images});
}

class Image {
  final int imageId;
  final String imageName;

  Image({this.imageId, this.imageName});
}

           

The factory method for

Image

will be非常簡單和基本。

factory Image.fromJson(Map<String, dynamic> parsedJson){
 return Image(
   imageId:parsedJson['id'],
   imageName:parsedJson['imageName']
 );
}

           

Now for the factory method for

Product

factory Product.fromJson(Map<String, dynamic> parsedJson){

  return Product(
    id: parsedJson['id'],
    name: parsedJson['name'],
    images: parsedJson['images']
  );
}

           

這顯然會引發運作時錯誤

type 'List<dynamic>' is not a subtype of type 'List<Image>'

           

如果我們這樣做,

images: Image.fromJson(parsedJson['images'])

           

這也絕對是錯誤的,它會立即引發錯誤,因為您無法将

Image

對象配置設定給

List<Image>

是以,我們必須建立一個

List<Image>

,然後将其配置設定給

images

var list = parsedJson['images'] as List;
print(list.runtimeType); //returns List<dynamic>List<Image> imagesList = list.map((i) => Image.fromJson(i)).toList();

           

list

這是一個List 。現在,我們周遊清單,每個對象映射

list

Image

調用

Image.fromJson

,(and map each object in

list

to

Image

by calling

Image.fromJson

)and then we把每個map object into 一個新的清單with

toList()

and 存儲in

List<Image> imagesList

. Find the full code here.

JSON結構#5: List of maps

現在讓我們轉到photo.json

[
  {
    "albumId": 1,
    "id": 1,
    "title": "accusamus beatae ad facilis cum similique qui sunt",
    "url": "http://placehold.it/600/92c952",
    "thumbnailUrl": "http://placehold.it/150/92c952"
  },
  {
    "albumId": 1,
    "id": 2,
    "title": "reprehenderit est deserunt velit ipsam",
    "url": "http://placehold.it/600/771796",
    "thumbnailUrl": "http://placehold.it/150/771796"
  },
  {
    "albumId": 1,
    "id": 3,
    "title": "officia porro iure quia iusto qui ipsa ut modi",
    "url": "http://placehold.it/600/24f355",
    "thumbnailUrl": "http://placehold.it/150/24f355"
  }
]

           

哦,哦。規則1和規則2告訴我這不可能是 a map 因為json字元串以方括号開頭這是對象清單嗎?是的 The object being here is

Photo

(或任何您想調用的對象).

class Photo{
  final String id;
  final String title;
  final String url;

  Photo({
    this.id,
    this.url,
    this.title
}) ;

  factory Photo.fromJson(Map<String, dynamic> json){
    return new Photo(
      id: json['id'].toString(),
      title: json['title'],
      url: json['json'],
    );
  }
}

           

但是它的清單

*Photo*

,這是否意味着您必須建構一個包含的類

*List<Photo>*

是的,我建議。

class PhotosList {
  final List<Photo> photos;

  PhotosList({
    this.photos,
  });
}

           

另請注意,此json字元串是List of maps. 。是以,在我們的工廠方法中,我們沒有

Map<String, dynamic>

參數,因為它是一個清單。這就是為什麼首先确定結構很重要的原因。是以,我們的新參數将為

List<dynamic>

factory PhotosList.fromJson(List<dynamic> parsedJson) {

    List<Photo> photos = new List<Photo>();

    return new PhotosList(
       photos: photos,
    );
  }

           

這會引發錯誤

Invalid value: Valid value range is empty: 0

           

嘿,因為我們永遠無法使用該

Photo.fromJson

方法。

如果在清單初始化後添加此行代碼怎麼辦?

photos = parsedJson.map((i)=>Photo.fromJson(i)).toList();

           

與之前的概念相同,我們不必将其映射到json字元串中的任何鍵,因為它是清單,而不是映射。Code here.

JSON結構#6:複雜的嵌套結構

Here is page.json.

我将要求您解決此問題。它已經包含在示例項目中。您隻需要為此構模組化型和服務檔案。但是在給您提示之前,我不會總結(如果需要,則需要任何提示)。

照常應用規則1和規則2。首先确定結構。這是一 a map. 是以,從1-5開始的所有json結構都會有所幫助。

規則3要求您首先建立類和構造函數,然後從最底層添加工廠方法。Just another tip. Also add the classes from the deep/bottom level. For e.g, for this json structure, make the class for

Image

first, then

Data

and

Author

and then the main class

Page

. 并按相同順序添加工廠方法。

對于類

Image

Data

請參考Json結構#4。

有關類,

Author

請參閱Json結構#3

初學者提示:在嘗試任何新資産時,請記住在pubspec.yaml檔案中進行聲明。

這篇Fluttery文章就是這樣。本文可能不是那裡最好的JSON解析文章,(因為我還在學習很多東西),但我希望它能幫助您入門。

如果您學到了一兩件事,請拍拍手👏盡可能多地表示支援!這激勵着我寫更多的東西。

此,從1-5開始的所有json結構都會有所幫助。

規則3要求您首先建立類和構造函數,然後從最底層添加工廠方法。Just another tip. Also add the classes from the deep/bottom level. For e.g, for this json structure, make the class for

Image

first, then

Data

and

Author

and then the main class

Page

. 并按相同順序添加工廠方法。

對于類

Image

Data

請參考Json結構#4。

有關類,

Author

請參閱Json結構#3

參考:https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51

在Flutter中解析複雜的JSONJSON結構#1:簡單 map通路對象JSON結構#2:具有數組的簡單結構Json結構#3:簡單嵌套結構JSON結構#4:具有清單的嵌套結構JSON結構#5: List of mapsJSON結構#6:複雜的嵌套結構FlutterJson數組轉換為List對象及Dio請求結果換為List對象

https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51

複雜JSON解析?

如何解析對象中的數組?

{
  "url": "xxx",
  "tabs": [
    {
      "labelName": "推薦",
      "groupChannelCode": "tourphoto_global1"
    },
    {
      "labelName": "拍照技巧",
      "groupChannelCode": "tab-photo"
    }
  ]
}
           

解析:

class TravelTabModel {
  String url;
  List<TravelTab> tabs;
 
  TravelTabModel({this.url, this.tabs});
 
  TravelTabModel.fromJson(Map<String, dynamic> json) {
     url = json['url'];
    (json['tabs'] as List).map((i) => TravelTab.fromJson(i));
  }
}
 
class TravelTab {
  String labelName;
  String groupChannelCode;
 
  TravelTab({this.labelName, this.groupChannelCode});
 
  TravelTab.fromJson(Map<String, dynamic> json) {
    labelName = json['labelName'];
    groupChannelCode = json['groupChannelCode'];
  }
}
           

如果要加些異常處理:

TravelTabModel.fromJson(Map<String, dynamic> json) {
    url = json['url'];
    if (json['tabs'] != null) {
      tabs = new List<TravelTab>();
      json['tabs'].forEach((v) {
        tabs.add(new TravelTab.fromJson(v));
      });
    }
  }
           

改成final:

class TravelTabModel {
  final String url;
  final List<TravelTab> tabs;
 
  TravelTabModel({this.url, this.tabs});
 
  factory TravelTabModel.fromJson(Map<String, dynamic> json) {
    String url = json['url'];
    List<TravelTab> tabs =
        (json['tabs'] as List).map((i) => TravelTab.fromJson(i)).toList();
    return TravelTabModel(url: url, tabs: tabs);
  }
}
           

FlutterJson數組轉換為List對象及Dio請求結果換為List對象

1.實體類

class VideoInfo {
  String body;
  int id;
  String title;
  int userId;

  VideoInfo({this.body, this.id, this.title, this.userId});

  factory VideoInfo.fromJson(Map<String, dynamic> json) {
    return VideoInfo(
      body: json['body'],
      id: json['id'],
      title: json['title'],
      userId: json['userId'],
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['body'] = this.body;
    data['id'] = this.id;
    data['title'] = this.title;
    data['userId'] = this.userId;
    return data;
  }
}
           

1.1.Json數組轉List對象

假設我們的Json數組是這樣的:

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  }
 ]
           

将json數組轉為List對象的方法如下:

String jsonStr =
            '[{"userId": 1,"id": 1,"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit","body": "quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto"},{"userId": 1,"id": 2,"title": "qui est esse","body": "est rerum tempore vitae sequi sint nihil reprehenderit dolor beatae ea dolores neque fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis qui aperiam non debitis possimus qui neque nisi nulla"}]';

///  或json.decode(s);  實際的類型是List<dynamic>
var listDynamic = jsonDecode(jsonStr);
/// 顯式類型轉換, List<dynamic>   ->  List<Map<String, dynamic>>
List<Map<String, dynamic>> listMap =
            new List<Map<String, dynamic>>.from(listDynamic);
List<VideoInfo> M = new List();
listMap.forEach((m) => M.add(VideoInfo.fromJson(m)));
print("資料為${M[0].title}");
           

或者用以下方法,更為簡潔高效:

String jsonStr =
            '[{"userId": 1,"id": 1,"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit","body": "quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto"},{"userId": 1,"id": 2,"title": "qui est esse","body": "est rerum tempore vitae sequi sint nihil reprehenderit dolor beatae ea dolores neque fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis qui aperiam non debitis possimus qui neque nisi nulla"}]';
///  或json.decode(s);  實際的類型是List<dynamic>
var listDynamic = jsonDecode(jsonStr);
List<VideoInfo> MM = (listDynamic as List<dynamic>).map((e) => VideoInfo.fromJson((e as Map<String,dynamic>))).toList();
print("資料為${MM[0].title}");
           

1.2.Dio請求結果轉List對象

loadData() async {
    String dataURL = "https://jsonplaceholder.typicode.com/posts";
    try {
      Response response = await Dio().get(
        dataURL,
        options: Options(responseType: ResponseType.json),
      );
      if (response.statusCode == 200) {
        print("響應資料:${response.data}");
        /// 更新UI
        setState(() {
          /// 解析資料
          List<VideoInfo> list = (response.data as List<dynamic>)
              .map((e) => VideoInfo.fromJson((e as Map<String, dynamic>)))
              .toList();
          print("My Data:${list[0].title}");
        });
      } else {
        print("失敗${response.statusCode}");
      }
    } catch (e) {
      print(e);
    }
  }
           

在上面的請求中,Dio傳回的結果為List<dynamic>,我想要的結果是List<VideoInfo>,那麼我們的思路就是:

1.因為拿回來的資料Response沒有指定泛型,是以預設為dynamic,是以我們要将其類型轉換回List<dynamic>,即:

response.data as List<dynamic>

如果我們指定了Response的泛型為List<dynamic>,則不需要做這一步的工作,即Response<List<dynamic>>:

Response<List<dynamic>> response = await Dio().get(
        dataURL,
        options: Options(responseType: ResponseType.json),
      );
           

1.首先将List<dynamic>中的泛型dynamic轉換成Map<String,dynamic>,也就是将List<dynamic>轉成List<Map<String,dynamic>>,即e as Map<String, dynamic>,再利用我們的對象VideoInfo.fromJson()方法,生成我們的對象,即:

VideoInfo.fromJson((e as Map<String, dynamic>))
           

完成的轉換過程為:

 /// 解析資料
List<VideoInfo> list = (response.data as List<dynamic>)
              .map((e) => VideoInfo.fromJson((e as Map<String, dynamic>)))
              .toList();
           

轉換好的結果是在疊代器Iterable<E>裡的,是以我們可以用疊代器提供的接口toList()将結果轉換為List對象。

2.總結

将JSON數組轉成List對象的關鍵就是利用VideoInfo.fromJson(),将Map對象轉成VideoInfo對象。這當種涉及到類型轉換,尤其是dynamic類型的轉換。要識别好各個變量的真實類型,這至為重要。