【问题标题】:JSON serialization and deserialization to objects in FlutterFlutter中对象的JSON序列化和反序列化
【发布时间】:2017-11-19 10:30:53
【问题描述】:

自从 Flutter 从其 SDK 中取出 dart: mirrors 后,不再可能使用 dartson 之类的库进行 JSON 对象序列化/反序列化。但是,我读过 built_value 是实现类似目的的另一种方式。我找不到任何关于如何实现它的好例子,因为它包含大量的样板代码。有人可以给我一个例子吗?例如,这是我试图序列化为对象的 JSON:

{
    "name":"John",
    "age":30,
    "cars": [
        { "name":"Ford", "models":[ "Fiesta", "Focus", "Mustang" ] },
        { "name":"BMW", "models":[ "320", "X3", "X5" ] },
        { "name":"Fiat", "models":[ "500", "Panda" ] }
    ]
 }

【问题讨论】:

    标签: json serialization dart flutter


    【解决方案1】:

    json_serialization

    This package 由 Dart 团队在单独的文件中生成 fromJson 构造函数和 toJson 方法所需的一切。

    依赖关系

    添加以下依赖项:

    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';
    
    // will be generated later
    part 'person.g.dart';
    
    @JsonSerializable()
    class Person {
      Person(this.name, this.age);
    
      final String name;
      final int age;
    
      factory Person.fromJson(Map<String, dynamic> json) =>
          _$PersonFromJson(json);
    
      Map<String, dynamic> toJson() => _$PersonToJson(this);
    }
    

    生成代码

    从终端生成person.g.dart文件:

    flutter packages pub run build_runner build
    

    使用它

    然后像这样使用它:

    JSON → 对象

    String rawJson = '{"name":"Mary","age":30}';
    Map<String, dynamic> map = jsonDecode(rawJson);
    Person person = Person.fromJson(map);
    

    对象 → JSON

    Person person = Person('Mary', 30);
    Map<String, dynamic> map = person.toJson();
    String rawJson = jsonEncode(map);
    

    注意事项

    • 在 Dart 项目中使用 pub run build_runner build
    • 有关序列化 JSON 的更多方法,请参阅 this answer

    【讨论】:

    • 如果我收到人员列表怎么办?如何使用这个包反序列化它?比如说这个:[{"name":"Mary","age":30}, {"name":"Joe","age":20}] 谢谢 :)
    • @hkop,要反序列化 json 对象列表,请查看 this Q&A
    【解决方案2】:

    来自Dart web site

    dart:convert 库提供了一个 JsonCodec 类,您可以使用它自动将简单类型(map、list、int、num、string)从 a 和 JSON 字符串转换为 JSON 字符串。两个关键的静态方法是JSON.encode(object)JSON.decode(string)

    解码示例:

    import 'dart:convert';
    ...    
    Map<String, dynamic> parsedMap = JSON.decode(json);
    print(parsedMap['name']); // John
    print(parsedMap['age']); // 30
    

    编码示例:

    Map<String, dynamic> mapData = <String, dynamic>{ 'hello': 'world!' };
    String jsonData = JSON.encode(mapData); // convert map to String
    

    如果你想让你的 JSON 扩展为自定义 Dart 类而不是原始对象树,Hadrien 的回答应该为你指明正确的方向,但我只是想把它留在这里,以防其他人试图获得基本的JSON 序列化/反序列化工作。

    【讨论】:

      【解决方案3】:

      您可以使用 Jaguar Serializer,它很容易上手,非常适合 Flutter 或服务器和 Web 开发。

      https://github.com/Jaguar-dart/jaguar_serializer

      【讨论】:

      • 这个库看起来很有趣!我试过了,但是当我运行序列化程序构建时出现此错误:'package:jaguar_generator_config/src/generator.dart':错误:第 17 行:非法隐式访问接收器'this'_config = loadYaml(new File(configFileName ).readAsStringSync());
      【解决方案4】:

      我希望从所提供的答案中获得更多详细信息。尽管它们是很好的建议,但它们太笼统了,我无法理解。因此,在进行了自己的研究之后,我将把我的实现分享到我提供的上述 JSON 示例中,希望它可以节省其他人的时间。所以这是我遵循的步骤:

      • 在我的 Flutter 项目中,我首先导入了以下库:

      依赖:

      内置值:^1.0.1
      内置集合:^1.0.0

      dev_dependencies:

      build_runner:^0.3.0
      built_value_generator:^1.0.1

      • 我创建了一个名为 tool 的文件夹。在其中,我放了 2 个文件:build.dart 和 watch.dart。这些文件的实现如下所示

      build.dart

      // Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.
      // All rights reserved. Use of this source code is governed by a BSD-style
      // license that can be found in the LICENSE file.
      
      import 'dart:async';
      
      import 'package:build_runner/build_runner.dart';
      import 'package:built_value_generator/built_value_generator.dart';
      import 'package:source_gen/source_gen.dart';
      
      /// Example of how to use source_gen with [BuiltValueGenerator].
      ///
      /// Import the generators you want and pass them to [build] as shown,
      /// specifying which files in which packages you want to run against.
      Future main(List<String> args) async {
        await build(
            new PhaseGroup.singleAction(
                new GeneratorBuilder([new BuiltValueGenerator()]),
                new InputSet('built_value_example', const [
                  'lib/model/*.dart',
                  'lib/*.dart',
                ])),
            deleteFilesByDefault: true);
      }
      

      watch.dart

      // Copyright (c) 2016, Google Inc. Please see the AUTHORS file for details.
      // All rights reserved. Use of this source code is governed by a BSD-style
      // license that can be found in the LICENSE file.
      
      import 'dart:async';
      
      import 'package:build_runner/build_runner.dart';
      import 'package:built_value_generator/built_value_generator.dart';
      import 'package:source_gen/source_gen.dart';
      
      /// Example of how to use source_gen with [BuiltValueGenerator].
      ///
      /// This script runs a watcher that continuously rebuilds generated source.
      ///
      /// Import the generators you want and pass them to [watch] as shown,
      /// specifying which files in which packages you want to run against.
      Future main(List<String> args) async {
        watch(
            new PhaseGroup.singleAction(
                new GeneratorBuilder([new BuiltValueGenerator()]),
                new InputSet('built_value_example', const [
                  'lib/model/*.dart',
                  'lib/*.dart'])),
            deleteFilesByDefault: true);
      }
      
      • 我创建了一个 serializers.dart 文件,它将我的 json 字符串序列化为我的自定义 dart 对象和我的模型对象 person.dart

      serializers.dart

      library serializers;
      
      import 'package:built_collection/built_collection.dart';
      import 'package:built_value/serializer.dart';
      import 'package:built_value/standard_json_plugin.dart';
      import 'model/person.dart';
      
      part 'serializers.g.dart';
      
      Serializers serializers = (
          _$serializers.toBuilder()..addPlugin(new StandardJsonPlugin())
      ).build();
      

      person.dart

      library person;
      
      import 'package:built_collection/built_collection.dart';
      import 'package:built_value/built_value.dart';
      import 'package:built_value/serializer.dart';
      
      part 'person.g.dart';
      
      abstract class Person implements Built<Person, PersonBuilder> {
        String get name;
        int get age;
        BuiltList<Car> get cars;
      
        Person._();
        factory Person([updates(PersonBuilder b)]) = _$Person;
        static Serializer<Person> get serializer => _$personSerializer;
      }
      
      abstract class Car implements Built<Car, CarBuilder> {
        String get name;
        BuiltList<String> get models;
      
        Car._();
        factory Car([updates(CarBuilder b)]) = _$Car;
        static Serializer<Car> get serializer => _$carSerializer;
      }
      
      • 创建上述4个文件后,会显示一些编译器错误。不要介意他们。这是因为 build.dart 文件尚未运行。所以在这一步中,运行 build.dart。如果您使用 Webstorm,只需右键单击 build.dart 并点击“运行 build.dart”。这将创建 2 个文件:“person.g.dart”和“serializers.g.dart”。如果您仔细注意到,在我们的 build.dart 文件中,我们放置了 'lib/model/.dart' 和 'lib/.dart'。构建通过指定的路径知道在哪里查找这些文件,并查找包含部分“某些东西”的文件。因此,在运行 build.dart 文件之前将该行保留在这些文件中很重要

      • 最后,现在我可以使用 main.dart 文件中的序列化程序将 json 字符串序列化为我的自定义 dart 对象类 Person。在我的 main.dart 中,我在 initState() 中添加了以下代码

      main.dart

        Person _person;
      
        @override
        void initState() {
          super.initState();
          String json = "{"
              "\"name\":\"John\",\"age\":30,\"cars\": "
              "["
              "{ \"name\":\"Ford\", \"models\":[ \"Fiesta\", \"Focus\", \"Mustang\" ] },"
              "{ \"name\":\"BMW\", \"models\":[ \"320\", \"X3\", \"X5\" ] },"
              "{ \"name\":\"Fiat\", \"models\":[ \"500\", \"Panda\" ] }"
              "]}";
      
          setState(() {
            _person = serializers.deserializeWith(
                Person.serializer, JSON.decode(json));
          });
        }
      

      我的示例项目也可以在 Github Built value sample project上找到

      【讨论】:

      • 非常感谢您分享您的解决方案。但是,当我想实例化一个 Person 类并将值设置为 age 例如时,我遇到了一个问题。这是不可能的,因为缺少二传手。你有什么解决方案来解决这个问题吗?我想在我的项目中使用相同的类并进行序列化
      • built_value 使用不可变对象。这是一种设计选择。这样,您的代码中就不会出现可能导致意外行为的变异对象,因为每次您想要更改值时都必须实例化一个新对象。
      【解决方案5】:

      您应该为 Built_value 准备一个配置文件,该文件将解析您的 dart 源并生成 .g.dart。一旦准备好 json 序列化是自动的。您可以生成这些文件一次或使用 watch 命令。

      这些文件将被添加到与源和 dart 命令相同的级别

      part of data;
      

      被视为同一个类。

      这是我在 Flutter 项目中使用的配置:

      import 'dart:async';
      
      import 'package:build_runner/build_runner.dart';
      import 'package:built_value_generator/built_value_generator.dart';
      import 'package:source_gen/source_gen.dart';
      
      
      Future main(List<String> args) async {
      
      await build(
      
      new PhaseGroup.singleAction(
      
      new GeneratorBuilder([
      
      new BuiltValueGenerator(),
      
      ]),
      
      new InputSet('flutter_project', const ['lib/data/*.dart'])),
      
      deleteFilesByDefault: true);
      
      }
      

      您可能会发现阅读 David Morgan 的所有帖子以了解其好处很有用。它需要一些时间来改变你的想法,但这是一个非常好的模式。

      https://medium.com/dartlang/darts-built-value-for-immutable-object-models-83e2497922d4

      https://medium.com/dartlang/darts-built-value-for-serialization-f5db9d0f4159

      诀窍是了解 sourcegen 如何解析,然后通过添加大量行为(如 Builders 和 Serializers)来丰富您的类。

      【讨论】:

      • 嗨罗伯特。感谢您的回复!我仍然对它是如何工作的感到困惑。抱歉,我对 Flutter 和 Dart 有点陌生。您能否在答案中添加更多详细信息?或者指向我提供更多详细信息的其他链接?我仍然不确定这如何与我给出的示例 JSON 一起使用
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-20
      • 1970-01-01
      • 2011-08-14
      • 1970-01-01
      • 1970-01-01
      • 2011-06-19
      • 1970-01-01
      相关资源
      最近更新 更多