How to serialize ExpandoObject using ServiceStack

2019-04-28 00:07发布

问题:

Is it possible to get the ServiceStack JsonSerializer to serialize an ExpandoObject as a flat object rather than a dictionary? Something roughly approximate to this:

{"x":"xvalue","y":"\/Date(1313966045485)\/"}

I am trying to compare JSON serialization of ExpandoObject using three different systems: the .NET BCL JavaScriptSerializer, Newtonsoft JSON.NET, and ServiceStack's JSON offering.

I start with a fairly simple dynamic object.

dynamic test = new ExpandoObject();
test.x = "xvalue";
test.y = DateTime.Now;

It seems simpler for a serializer to treat an ExpandoObject as a IDictionary<string, object>. Both BCL and ServiceStack start off this way, though going fairly different routes with the result.

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
Console.WriteLine(javaScriptSerializer.Serialize(test));
// [{"Key":"x","Value":"xvalue"},{"Key":"y","Value":"\/Date(1313966045485)\/"}]

Console.WriteLine(ServiceStack.Text.JsonSerializer.SerializeToString(test));
// ["[x, xvalue]","[y, 8/21/2011 16:59:34 PM]"]

I would prefer to have ExpandoObject serialized more as it is assembled in code, like a typical class would be serialized. You can add an override JavaScript serializer to the BCL system for IDictionary<string, object>. This works great, assuming one doesn't actually have a IDictionary<string, object> that needs to stay that way (which I don't yet).

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
javaScriptSerializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJsonConverter() });
Console.WriteLine(javaScriptSerializer.Serialize(test));
// {"x":"xvalue","y":"\/Date(1313966045485)\/"}

Unfortunately, I still need a way to get ServiceStack's JsonSerializer to treat an ExpandoObject in the same fashion. How do I hook into the ServiceStack system to make this possible?

Update: While it isn't an option for my uses, it looks like ServiceStack handles anonymous objects just fine.

Console.WriteLine(ServiceStack.Text.JsonSerializer.SerializeToString(new { x = "xvalue", y = DateTime.Now }));
// {"x":"xvalue","y":"\/Date(1313980029620+0000)\/"}

回答1:

There aren't any hooks available in ServiceStack's JsonSerializer that you can plug into to change this behaviour but I'm be more than happy to take a pull request that does it ;)

The classes you will need to change are:

  • JsWriter.cs (Line 223) - insert code to detect an expando object and return a delegate that writes the expando object how you want it.
  • JsReader (Line 42) - insert code to detect an expando object and return a delegate to read the contents of a string and deserialize it back into an expando

I imagine the code for dealing with an expando object is similar to a dictionary so you should be able to use the Dictionary implementation classes to guide you (i.e. WriteDictionary.cs and DeserializeDictionary.cs)

Feel free to use the ServiceStack forum if you have any more questions/issues on this: https://groups.google.com/forum/#!forum/servicestack