我不得不承认,在Flutter / Dart中利用JSON后,我错过了Android 的gson 天下。当我开始利用Flutter中的API时,JSON解析确实让我很烦恼。而且我确定,它让很多初学者感到困惑。
我们将利用dart:convert的内置库。这是最基本的解析方法,只有在您开始利用Flutter或者您正在构建一个小项目时才建议利用它。然而,理解Flutter中JSON解析的根本知识非常主要。当你善于这个,或者你须要利用更大的项目时,可以考虑像json_serializable等代码天生器库。如果可能的话,我将在往后的文章中先容它们。
示例项目。它包含您可以试验的本文的所有代码。

JSON构造#1:大略的舆图
让我们从student.json的大略JSON构造开始
{ \公众id\公众:\公众487349\公众, \"大众name\公众:\"大众Pooja Bhaumik\"大众, \"大众score\公众 : 1000}
规则#1: 确定构造。Json字符串将具有Map(键值对)或List of Maps。
规则#2:用花括号开始?这是Map。
用方括号开始?这是List of Map。
student.json显然是Map。(例如,id是一个Key,487349是id的值)
让我们为这个json构造制作一个PODO(Plain Old Dart Object?)文件。您可以在示例项目的[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'] ); }
在这里,我们创建一个名为factory的工厂方法,Student.fromJson其目的是大略地反序列化你的json。
我是一个小菜鸟,你能见告我关于反序列化的事吗?
当然。我们首先见告您序列化和反序列化。序列化只是意味着将数据(可能在工具中)写为字符串,而反序列化则与此相反。它获取原始数据并重修工具模型。在本文中,我们紧张谈论反序列化部分。在第一部分中,我们将json字符串反序列化student.json
因此我们的工厂方法可以称为我们的转换器方法。
还必须把稳fromJson方法中的参数。这是一个Map<String, dynamic>意味着它用一个值映射一个String 键。这正是我们须要识别构造的缘故原由。如果这个json构造是一个映射列表,那么这个参数会有所不同。dynamic
但为何(dynamic)动态?让我们先看看另一个json构造来回答你的问题。
name是Map <String,String>,majors是String和List <String> subjects的Map,是String和List <Object>的Map
由于key始终为 string且值可以是任何类型,因此我们将其保持为dynamic安全。
检讨完全代码student_model.dart 在这里。
访问工具
让我们编写student_services.dart哪些代码可以调用Student.fromJson并从Student工具中检索值。
imports
import 'dart:async' show Future;import 'package:flutter/services.dart' show rootBundle;import 'dart:convert';import 'package:flutter_json/student_model.dart';
末了一个导入是你的模型文件。
加载Json Asset(可选)
Future<String> _loadAStudentAsset() async { return await rootBundle.loadString('assets/student.json');}
在这个的项目中,我们将json文件放在assets文件夹中,因此我们必须以这种办法加载json。但是,如果您在云上拥有json文件,则可以进行通过网络读取。网络调用超出了本文的范围,这里不作先容。
加载
Future loadStudent() async { String jsonString = await _loadAStudentAsset(); final jsonResponse = json.decode(jsonString); Student student = new Student.fromJson(jsonResponse); print(student.studentScores);}
在此loadStudent()方法中,
第1行 :从Asset中加载原始json String。
第2行 :解码我们得到的这个原始json字符串。
第3行 :现在我们通过调用Student.fromJson方法对解码的json相应进行反序列化,以便我们现在可以利用Studentobject来访问我们的实体。
第4行 :就像我们在这里做的那样,我们studentScores从Student实例上打印出来。
检讨Flutter掌握台查看所有打印值。(在Android Studio中,在“运行”Tab页面)
瞧!
您刚刚进行了第一次JSON解析。
把稳:请记住这里的3个片段,我们将利用它进行下一组json解析(仅变动文件名和方法名称),我不会在这里再次重复代码。但无论如何,您可以在示例项目中找到所有内容。
JSON构造#2:具有数组的大略构造
现在我们征服一个类似于上面的json构造,但它不仅仅是单个值,它也可能有一个值数组。
{ \公众city\"大众: \"大众Mumbai\公众, \公众streets\公众: [ \"大众address1\"大众, \"大众address2\"大众 ]}
以是在这个address.json中,我们有一个city实体,它有一个大略的String值,但是streets是一个数组String。
据我所知,Dart没有数组数据类型,而是有一个List <datatype>以是这里streets将是一个List<String>。
现在我们必须检讨规则#1和规则#2 。这绝对是Map,由于这是从花括号开始的。streets仍旧是一个List,但我们稍后会进行谈论。
以是address_model.dart最初看起来会像这样
class Address { final String city; final List<String> streets; Address({ this.city, this.streets });}
既然这是Map,我们的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将会如何?
好吧,让我们分解一下。
在我们shape_model.dart 的类中,让我们定义一个Property得类。
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基本上是我们上一个类的工具Property。
规则#3:对付嵌套构造,首先创建类和布局函数,然后从底层添加工厂方法。
从底层来看,我们的意思是,首先我们解析Property类,然后我们进入Shape这一级。这只是我的建议,而不是Flutter规则。
factory Property.fromJson(Map<String, dynamic> json){ return Property( width: json['width'], breadth: json['breadth'] );}
这是一个大略map。
但对付我们在Shape类的工厂方法,我们不能这样做。
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'
第二,我们刚刚为Property制作了这个俊秀的小类,我没有看到它在任何地方利用。
对。我们必须在这里映射我们的Property类。
factory Shape.fromJson(Map<String, dynamic> parsedJson){ return Shape( shapeName: parsedJson['shape_name'], property: Property.fromJson(parsedJson['property']) );}
以是基本上,我们Property.fromJson从我们的Property类中调用方法,无论我们得到什么,我们都将它映射到property实体。大略!
在这里查看代码。
shape_services.dart和你一起跑吧,你很高兴。
JSON构造#4:带有列表的嵌套构造
我们来看看我们的product.json
{ \"大众id\"大众:1, \"大众name\"大众:\"大众ProductName\公众, \公众images\"大众:[ { \"大众id\"大众:11, \"大众imageName\公众:\"大众xCh-rhy\"大众 }, { \"大众id\"大众:31, \"大众imageName\"大众:\公众fjs-eun\"大众 } ]}
好的,现在我们越来越深入了。我在里面看到了一个工具列表。
是的,以是这个构造有一个工具列表,但它本身仍旧是一个Map。(拜会规则#1和规则#2)。现在参考规则#3,让我们构建我们的product_model.dart。
以是我们创建了两个新类Product和Image。
把稳:Product将有一个List ofImage
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});}
工厂方法Image非常大略和基本。
factory Image.fromJson(Map<String, dynamic> parsedJson){ return Image( imageId:parsedJson['id'], imageName:parsedJson['imageName'] );}
现在为工厂实现方法 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工具分配给aList<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 <dynamic>。现在,我们遍历列表,并在每个工具映射list到Image调用Image.fromJson,然后我们把每个舆图工具与一个新的列表toList()并将其存储在List<Image> imagesList。在这里找到完全的代码。
JSON构造#5:舆图列表
现在让我们来看看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见告我这不能是一个映射,由于json字符串以方括号开头。那么这是一个工具列表?是。这里的工具是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字符串是一个映射列表。以是,在我们的工厂方法中,我们没有Map<String, dynamic>参数,由于它是一个List。这便是为什么首先确定构造很主要的缘故原由。以是我们的新参数将是一个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字符串中的任何键,由于它是List而不是map。代码在这里。
JSON构造#6:繁芜的嵌套构造
这是page.json。
我会哀求你办理这个问题。它已包含在示例项目中。您只需为此构建模型和做事文件。但是在给你提示和提示之前我不会总结(如果是的话,你须要任何提示)。
常日适用规则#1和规则#2。首先确定构造。这是Map。以是1-5的所有json构造都会有所帮助。
规则#3哀求您首先创建类和布局函数,然后从底层添加工厂方法。只是另一个提示。还要从深层/底层添加类。例如,对付这个JSON构造,使类Image,然后再Data和Author再主类Page。并以相同的顺序添加工厂方法。
对付类而言Image,Data请参考Json构造#4。
对付类,Author请参阅Json构造#3
初学者提示:在考试测验任何新Asset时,请记住在pubspec.yaml文件中声明它。
译:https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51