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

Max*_*Max 44 json dart

谈到Dart中的JSON编码,根据Seth Ladd的说法,最终批准的官方方式是dart:convert+ JSON.Encode.

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

class Customer
{
  int Id;
  String Name;
}
Run Code Online (Sandbox Code Playgroud)

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

var customer = new Customer()
  ..Id = 17
  ..Name = "John";
var json = JSON.encode(customer);
Run Code Online (Sandbox Code Playgroud)

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

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)
....
Run Code Online (Sandbox Code Playgroud)

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

class Customer
{
  int Id;
  String Name;

  Map toJson() { 
    Map map = new Map();
    map["Id"] = Id;
    map["Name"] = Name;
    return map;
  }  
}
Run Code Online (Sandbox Code Playgroud)

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

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

{
    "Id": 17,
    "Name": "John"
}
Run Code Online (Sandbox Code Playgroud)

例如,ToJsonServiceStack.Text中进行比较.

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

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

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

[17,"John"]
Run Code Online (Sandbox Code Playgroud)

另一方面,使用默认的SimpleMapFormat生成此复杂表示.

仍然没有找到我正在寻找...

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

编辑3:我写了一篇关于此的小博客文章.另见关于黑客新闻的讨论.

myt*_*thz 27

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

没有反射和镜像支持

Flutter这样的流行Dart平台不支持Reflection/Mirrors,你唯一的选择就是使用代码解决方案.我们在ServiceStack 对Dart和Flutter本机支持中采用的方法允许您从远程URL为所有ServiceStack服务生成类型化的Dart模型,例如:

$ npm install -g @servicestack/cli

$ dart-ref https://www.techstacks.io
Run Code Online (Sandbox Code Playgroud)

.NET Core和任何.NET流行的托管选项都支持.

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

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;
}
Run Code Online (Sandbox Code Playgroud)

使用此模型,您可以使用Dart内置的json:convert API将模型序列化和反序列化为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);
Run Code Online (Sandbox Code Playgroud)

这种方法的好处是它适用于所有Dart平台,包括Flutter和AngularDart或Dart Web Apps,有或没有Dart 2的强模式.

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

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

有关更多信息,请参阅ServiceStack的本机Dart支持的文档.

用镜子飞镖

如果您在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;
  }  

}
Run Code Online (Sandbox Code Playgroud)

您可以将其与您的PODO课程混合使用:

class Customer extends Object with Serializable
{
  int Id;
  String Name;
}
Run Code Online (Sandbox Code Playgroud)

您现在可以使用哪个JSON.encode:

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

print(JSON.encode(c));
Run Code Online (Sandbox Code Playgroud)

结果:

{"Id":1,"Name":"Foo"}
Run Code Online (Sandbox Code Playgroud)

注意:请参阅使用Mirrors的警告

  • @SethLadd JSON的吸引力在于它是编程语言的完美编程.必须为每个类手动配置序列化会使目的失败,使其变得乏味且容易出错.确实应该有JSON的一流支持,其默认行为可以按预期工作,就像使用JavaScript一样. (5认同)
  • 我看到这是在2013年发布的,这仍然是(de)序列化为JSON的唯一方法,还是现在有更好的方法(2015年)? (2认同)

Lek*_*sat 14

我编写了可导出的库来解决诸如转换为Map或JSON之类的问题.使用它,模型声明如下所示:

import 'package:exportable/exportable.dart';

class Customer extends Object with Exportable {
  @export int id;
  @export String name;
}
Run Code Online (Sandbox Code Playgroud)

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

String jsonString = customer.toJson();
Run Code Online (Sandbox Code Playgroud)

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

Customer customer = new Customer()..initFromJson(jsonString);
Run Code Online (Sandbox Code Playgroud)

或者:

Customer customer = new Exportable(Customer, jsonString);
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅自述文件.

  • 与 Dart 2.0 不兼容 (2认同)

Mat*_*t B 6

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


Cri*_*cia 6

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

class News
{
    @Field() String title;
    @Field() String text;
    @Field() List<FileDb> images;
    @Field() String link; 
}
Run Code Online (Sandbox Code Playgroud)

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

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