Max Max - 8 days ago 4
Dart Question

Add JSON serializer to every model class?

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.

Answer

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.

Having said 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