将JSON序列化程序添加到每个模型类?

时间:2013-11-16 21:46:33

标签: json dart

对于Dart中的JSON编码,根据Seth Ladd's accouncement,最终批准的正式方式是dart:convert + JSON.Encode

假设我们有一堆模型类(PODO s),例如:

class Customer
{
  int Id;
  String Name;
}

现在,我希望能够对我的域对象进行JSON编码,如下所示:

var customer = new Customer()
  ..Id = 17
  ..Name = "John";
var json = JSON.encode(customer);

不幸的是,这不起作用......

Uncaught Error: Converting object to an encodable object failed.
Stack Trace: 
#0      _JsonStringifier.stringifyValue (dart:convert/json.dart:416)
#1      _JsonStringifier.stringify (dart:convert/json.dart:336)
#2      JsonEncoder.convert (dart:convert/json.dart:177)
....

...除非我们明确告诉dart:convert如何编码:

class Customer
{
  int Id;
  String Name;

  Map toJson() { 
    Map map = new Map();
    map["Id"] = Id;
    map["Name"] = Name;
    return map;
  }  
}

我是否真的必须为我的每个模型类添加toJson方法,还是有更好的方法?

编辑:这是我正在寻找的简单序列化:

{
    "Id": 17,
    "Name": "John"
}

例如,与ServiceStack.Text中的ToJson进行比较。

Dart的serialization库(见下面的Matt B答案)似乎是朝着正确方向迈出的一步。但是,这......

var serialization = new Serialization()
  ..addRuleFor(Customer); 
var json = JSON.encode(serialization.write(customer, format: new SimpleJsonFormat()));

...只生成一个带有值(无键)的数组:

[17,"John"]

另一方面,使用默认SimpleMapFormat生成此complex表示。

还没找到我要找的东西......

编辑2 :添加一些上下文:我正在Dart中构建一个RESTful Web服务,我正在寻找一个可以被任何客户端轻松使用的JSON序列化,而不仅仅是另一个Dart客户。例如,查询Stack Exchange API以获取此问题将创建this JSON response。这是我正在寻找的序列化格式。 - 或者,查看Twitter REST APIFacebook Graph API返回的典型JSON响应。

编辑3 :我写了一篇关于此的小blog post。另请参阅黑客新闻的discussion

9 个答案:

答案 0 :(得分:27)

IMO这是Dart的一个重大缺陷,鉴于其Web应用程序的重点,令人惊讶。我认为在标准库中使用JSON支持将意味着将类与JSON序列化将像水一样工作,不幸的是,JSON支持似乎不完整,看起来选择是使用松散类型的地图或遭受通过不必要的样板来配置标准(PODO)类以按预期序列化。

没有反射和镜像支持

Flutter这样受欢迎的Dart平台不支持Reflection / Mirrors,您唯一的选择就是使用代码解决方案。我们在ServiceStack的native support for Dart and Flutter中采用的方法允许您从远程URL为所有ServiceStack服务生成类型化的Dart模型,例如:

$ npm install -g @servicestack/cli

$ dart-ref https://www.techstacks.io
  

.NET Core和.NET's popular hosting options中的任何一个都支持。

上面的示例使用.NET Core 2.0 TechStacks project端点生成的DTO为www.techstacks.io/types/dart生成类型化API。这会生成遵循Dart JsonCodec pattern的模型,您可以通过提供fromJson命名构造函数和toJson()实例方法来自定义Dart模型的序列化,这里是生成的DTO之一的示例:< / p>

class UserInfo implements IConvertible
{
    String userName;
    String avatarUrl;
    int stacksCount;

    UserInfo({this.userName,this.avatarUrl,this.stacksCount});
    UserInfo.fromJson(Map<String, dynamic> json) { fromMap(json); }

    fromMap(Map<String, dynamic> json) {
        userName = json['userName'];
        avatarUrl = json['avatarUrl'];
        stacksCount = json['stacksCount'];
        return this;
    }

    Map<String, dynamic> toJson() => {
        'userName': userName,
        'avatarUrl': avatarUrl,
        'stacksCount': stacksCount
    };

    TypeContext context = _ctx;
}

使用此模型,您可以使用Dart内置的in json:convert APIs将模型序列化和反序列化为JSON,例如:

//Serialization
var dto = new UserInfo(userName:"foo",avatarUrl:profileUrl,stacksCount:10);
String jsonString = json.encode(dto);

//Deserialization
Map<String,dynamic> jsonObj = json.decode(jsonString);
var fromJson = new UserInfo.fromJson(jsonObj);

这种方法的好处是它可以在所有Dart平台中工作,包括有和没有Dart 2强模式的Flutter和AngularDart或Dart Web Apps。

生成的DTO还可以与servicestack's Dart package一起使用,以启用端到端类型的解决方案,该解决方案将JSON序列化输入和输出您键入的DTO,例如:

var client = new JsonServiceClient("https://www.techstacks.io");
var response = await client.get(new GetUserInfo(userName:"mythz"));

有关详细信息,请参阅ServiceStack's native Dart support的文档。

带镜子的飞镖

如果您在可以使用Mirrors支持的平台中使用Dart,我发现使用Mixin需要的工作量最少,例如:

import 'dart:convert';
import 'dart:mirrors';

abstract class Serializable {

  Map toJson() { 
    Map map = new Map();
    InstanceMirror im = reflect(this);
    ClassMirror cm = im.type;
    var decls = cm.declarations.values.where((dm) => dm is VariableMirror);
    decls.forEach((dm) {
      var key = MirrorSystem.getName(dm.simpleName);
      var val = im.getField(dm.simpleName).reflectee;
      map[key] = val;
    });

    return map;
  }  

}

您可以使用以下内容与PODO课程混合:

class Customer extends Object with Serializable
{
  int Id;
  String Name;
}

您现在可以使用JSON.encode

var c = new Customer()..Id = 1..Name = "Foo";

print(JSON.encode(c));

结果:

{"Id":1,"Name":"Foo"}

注意:see caveats with using Mirrors

答案 1 :(得分:14)

我编写了Exportable库来解决转换为Map或JSON等问题。使用它,模型声明如下:

import 'package:exportable/exportable.dart';

class Customer extends Object with Exportable {
  @export int id;
  @export String name;
}

如果你想转换为JSON,你可以:

String jsonString = customer.toJson();

此外,从JSON字符串初始化新对象很容易:

Customer customer = new Customer()..initFromJson(jsonString);

或者:

Customer customer = new Exportable(Customer, jsonString);

请参阅README了解详情。

答案 2 :(得分:6)

另一种方法是使用Serialization package并为您的类添加规则。最基本的表单使用反射来自动获取属性。

答案 3 :(得分:6)

Redstone mapper是我使用过的最好的序列化库。 JsonObject和Exportable有一个缺点,你必须扩展他们的一些类。使用Redstone Mapper,您可以拥有这样的结构

class News
{
    @Field() String title;
    @Field() String text;
    @Field() List<FileDb> images;
    @Field() String link; 
}

它适用于getter和setter,你可以通过不用@Field()注释它来隐藏信息,你可以从/到json重命名字段,有嵌套对象,它在服务器和客户端上工作。它还与Redstone Server框架集成,它具有对MongoDB进行编码/解码的帮助程序。

我在正确方向上看到的唯一其他框架是Dartson,但与Redstone Mapper相比,它仍然缺少一些功能。

答案 4 :(得分:3)

我已经解决了:

class Customer extends JsonObject
{
  int Id;
  String Name;
  Address Addr;
}

class Address extends JsonObject{
  String city;
  String State;
  String Street;
}

但我的目标是将来自/到json的数据绑定到/到模型类;如果您可以修改模型类,此解决方案有效,相比之下您必须使用解决方案“外部”来转换模型类;

另见:Parsing JSON list with JsonObject library in Dart

答案 5 :(得分:3)

解决此问题的另一个包是built_value:

https://github.com/google/built_value.dart

使用built_value,您的模型类如下所示:

abstract class Account implements Built<Account, AccountBuilder> {
  static Serializer<Account> get serializer => _$accountSerializer;

  int get id;
  String get name;
  BuiltMap<String, JsonObject> get keyValues;

  factory Account([updates(AccountBuilder b)]) = _$Account;
  Account._();
}

请注意,built_value不只是关于序列化 - 它还提供了operator ==,hashCode,toString和一个构建器类。

答案 6 :(得分:1)

我已经实现了:

要执行此操作,请在类声明的@JsonSerializable()批注中传递explicitToJson:true。现在,User类如下所示:

import 'address.dart';
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';

@JsonSerializable(explicitToJson: true)
class User {
  String firstName;
  Address address;

  User(this.firstName, this.address);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

您可以在此处查看:https://flutter.dev/docs/development/data-and-backend/json#generating-code-for-nested-classes

答案 7 :(得分:0)

我更喜欢使用自己编写的https://ashamp.github.io/jsonToDartModel/在线工具。

它具有以下功能:

  • 在线使用,无需插件
  • 支持多维列表
  • 支持复杂的json
  • 支持将所有道具转换为字符串类型
  • 空道具警告
  • 单个文件
  • dart关键字受保护
  • 即时转换

我认为它比其他工具要好。欢迎您提出任何建议,问题或错误报告。

答案 8 :(得分:0)

某些答案不再适用于Flutter 2;这是自动创建toJson和fromJson方法的过程:

https://flutter.dev/docs/development/data-and-backend/json#creating-model-classes-the-json_serializable-way

PS:我希望这就像在Asp.Net中使用Newtonsoft库一样简单,该解决方案最接近自动化解决方案