向每个模型类添加 JSON 序列化程序? [英] Add JSON serializer to every model class?

查看:26
本文介绍了向每个模型类添加 JSON 序列化程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当谈到 Dart 中的 JSON 编码时,根据 Seth Ladd 的声明最后现在批准的官方方法是 dart:convert + JSON.Encode.

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

class 客户{内部标识;字符串名称;}

现在,我希望能够像这样对我的域对象进行 JSON 编码:

var customer = new Customer()..Id = 17..Name = "约翰";var json = JSON.encode(客户);

不幸的是,这行不通...

未捕获的错误:将对象转换为可编码对象失败.堆栈跟踪:#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 客户{内部标识;字符串名称;映射到Json() {地图地图=新地图();地图["Id"] = Id;地图[名称"] = 名称;返回地图;}}

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

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

<代码>{身份证":17,姓名":约翰"}

例如,

ServiceStack.Text 中的 ToJson 进行比较.

Dart 的 serialization 库(请参阅下面的 Matt B 的回答)似乎是正确的方向.然而,这...

var 序列化 = new Serialization()..addRuleFor(客户);var json = JSON.encode(serialization.write(customer, format: new SimpleJsonFormat()));

... 只生成一个包含值的数组(没有键):

[17,"约翰"]

使用默认的SimpleMapFormat 另一方面生成这个 complex 表示.

仍然没有找到我要找的东西...

EDIT 2:添加一些上下文:我正在 Dart 中构建一个 RESTful Web 服务,我正在寻找一个 JSON 序列化,它可以被任何客户端轻松使用,而不仅仅是另一个 Dart客户.例如,针对这个问题查询 Stack Exchange API 将创建 这个 JSON回复.这是我正在寻找的序列化格式.- 或者,查看由 Twitter REST APIFacebook 图谱 API.

编辑 3:我写了一个小的 博客文章 关于这个.另请参阅 Hacker News 上的讨论.

解决方案

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

不支持反射和镜像

由于Flutter 等流行的 Dart 平台不支持反射/镜像,您唯一的选择是使用代码生成解决方案.我们在 ServiceStack 的 对 Dart 和 Flutter 的原生支持 中采用的方法让您从远程 URL 为所有 ServiceStack 服务生成类型化的 Dart 模型,例如:

$ npm install -g @servicestack/cli$ dart-ref https://techstacks.io

<块引用>

在 .NET Core 和任何 .NET 流行的托管选项中受支持.

以上示例使用从 .NET TechStacks 项目 生成的类型化 API 使用 techstacks.io/types/dart 端点.这会按照 Dart 的 JsonCodec 模式 生成模型,您可以在其中自定义序列化你的 Dart 模型通过提供一个 fromJson 命名构造函数和一个 toJson() 实例方法,这里是一个生成的 DTO 的例子:

class UserInfo 实现 IConvertible{字符串用户名;字符串 avatarUrl;int stacksCount;用户信息({this.userName,this.avatarUrl,this.stacksCount});UserInfo.fromJson(Map json) { fromMap(json);}fromMap(Map json) {用户名 = json['用户名'];avatarUrl = json['avatarUrl'];stacksCount = json['stacksCount'];返回这个;}映射<字符串,动态>toJson() =>{'用户名':用户名,'avatarUrl': avatarUrl,'stacksCount': 堆栈数};类型上下文上下文 = _ctx;}

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

//序列化var dto = new UserInfo(userName:foo",avatarUrl:profileUrl,stacksCount:10);String jsonString = json.encode(dto);//反序列化映射<字符串,动态>jsonObj = json.decode(jsonString);var fromJson = new UserInfo.fromJson(jsonObj);

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

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

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

有关更多信息,请参阅ServiceStack 的原生 Dart 支持的文档.>

用镜子飞镖

如果你在一个支持镜像的平台上使用 Dart,我发现使用 Mixin 需要最少的努力,例如:

import 'dart:convert';进口飞镖:镜子";抽象类可序列化{映射到Json() {地图地图=新地图();InstanceMirror im = 反射(这个);ClassMirror cm = im.type;var decls = cm.declarations.values.where((dm) => dm 是 VariableMirror);decls.forEach((dm) {var key = MirrorSystem.getName(dm.simpleName);var val = im.getField(dm.simpleName).reflectee;地图[键] = val;});返回地图;}}

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

class Customer 使用可序列化扩展对象{内部标识;字符串名称;}

您现在可以与 JSON.encode 一起使用:

var c = new Customer()..Id = 1..Name = "Foo";打印(JSON.encode(c));

结果:

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

注意:查看使用镜像的注意事项

When it comes to JSON encoding in Dart, per Seth Ladd's accouncement the finally approved now official way to go is dart:convert + JSON.Encode.

Let's say we have a bunch of model classes (PODOs) such as:

class Customer
{
  int Id;
  String Name;
}

Now, I'd love to be able to just JSON-encode my domain objects like this:

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

Unfortunately, this won't work...

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)
....

... unless we explicitly tell dart:convert how to encode:

class Customer
{
  int Id;
  String Name;

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

Do I really have to add a toJson method to every single one of my model classes, or is there a better way?

EDIT: this is the simple serialization I'm looking for:

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

Compare to ToJson in ServiceStack.Text, for instance.

Dart's serialization library (see Matt B's answer below) seems like a step in the right direction. However, this ...

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

... produces just an array with the values (no keys):

[17,"John"]

Using the default SimpleMapFormat on the other hand generates this complex representation.

Still haven't found what I'm looking for...

EDIT 2: Adding some context: I'm building a RESTful web service in Dart, and I'm looking for a JSON serialization which can easily be consumed by any client, not just another Dart client. For instance, querying the Stack Exchange API for this very question will create this JSON response. This is the serialization format I'm looking for. - Or, look at typical JSON responses returned by the Twitter REST API or the Facebook Graph API.

EDIT 3: I wrote a small blog post about this. See also the discussion on Hacker News.

解决方案

IMO this is a major short-coming in Dart, surprising given its Web Application focus. I would've thought that having JSON support in the standard libraries would've meant that serializing classes to and from JSON would work like water, unfortunately the JSON support seems incomplete, where it appears the choices are to work with loosely typed maps or suffer through un-necessary boilerplate to configure your standard (PODO) classes to serialize as expected.

Without Reflection and Mirrors support

As popular Dart platforms like Flutter doesn't support Reflection/Mirrors your only option is to use a code-gen solution. The approach we've taken in ServiceStack's native support for Dart and Flutter lets you generate typed Dart models for all your ServiceStack Services from a remote URL, e.g:

$ npm install -g @servicestack/cli

$ dart-ref https://techstacks.io

Supported in .NET Core and any of .NET's popular hosting options.

The example above generates a Typed API for the .NET TechStacks project using the generated DTOs from techstacks.io/types/dart endpoint. This generates models following Dart's JsonCodec pattern where you can customize serialization for your Dart models by providing a fromJson named constructor and a toJson() instance method, here's an example of one of the generated DTOs:

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;
}

With this model you can use Dart's built-in json:convert APIs to serialize and deserialize your model to JSON, e.g:

//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);

The benefit of this approach is that it works in all Dart platforms, including Flutter and AngularDart or Dart Web Apps with and without Dart 2’s Strong Mode.

The generated DTOs can also be used with servicestack's Dart package to enable an end to end typed solution which takes care JSON serialization into and out of your typed DTOs, e.g:

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

For more info see docs for ServiceStack's native Dart support.

Dart with Mirrors

If you're using Dart in a platform where Mirrors support is available I've found using a Mixin requires the least effort, e.g:

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;
  }  

}

Which you can mixin with your PODO classes with:

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

Which you can now use with JSON.encode:

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

Result:

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

Note: see caveats with using Mirrors

这篇关于向每个模型类添加 JSON 序列化程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆