Pavel Voronin Pavel Voronin - 26 days ago 11
JSON Question

How to add types information to JSON on serialization?

Angular requires

Date
objects in many places whereas JSON contains string representation of the date.

I want to add an array of properties which contain date values:

class Foo
{
public int IntProp {get;set;}
public DateTime? Prop1 {get;set;}
public DateTime Prop2 {get;set;}
public Bar Bar {set;set;}
}

class Bar
{
public DateTime Prop {get;set;}
public IEnumerable<DateTime?> Dates {get;set;}
}


Foo should then be serialized like this:

{
"IntProp": 1,
"Prop1": "...",
"Prop2": "...",
"Bar": {
"Prop": "..."
},

"<Dates>": [ "Prop1", "Prop2", "Bar.Prop", "Bar.Dates"]
}


This allows me to automatically convert strings to date objects at the client side without testing every property whether it is convertible to
Date
like it is described in this question.

I can collect the paths of date properties, but have no idea how to add populated array to the root.

dbc dbc
Answer

You could convert to an intermediate JObject and add the property there. For instance, given the following converter:

public class PathLoggingDateTimeConverter : IsoDateTimeConverter
{
    public const string DatePathPropertyName = "<Dates>";

    readonly List<string> paths = new List<string>();

    public override bool CanConvert(Type objectType)
    {
        if (!base.CanConvert(objectType))
            return false;
        // Not for DateTimeOffset
        return objectType == typeof(DateTime) || objectType == typeof(DateTime?);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        base.WriteJson(writer, value, serializer);
        if (value != null)
            paths.Add(writer.Path);
    }

    public IList<string> Paths { get { return paths; } }
}

You can do:

        var root = new Foo
        {
            IntProp = 101,
            Prop1 = DateTime.Today.ToUniversalTime(),
            Prop2 = DateTime.Today.ToUniversalTime(),
            Bar = new Bar
            {
                Prop = DateTime.Today.ToUniversalTime(),
                Dates = new List<DateTime?> { null, DateTime.Today.ToUniversalTime() },
            },
        };

        var converter = new PathLoggingDateTimeConverter();
        var settings = new JsonSerializerSettings { Converters = new[] { converter } };
        var obj = JObject.FromObject(root, JsonSerializer.CreateDefault(settings));
        obj[PathLoggingDateTimeConverter.DatePathPropertyName] = JToken.FromObject(converter.Paths);

        Console.WriteLine(obj);

And the result is:

{
  "IntProp": 101,
  "Prop1": "2016-10-25T04:00:00Z",
  "Prop2": "2016-10-25T04:00:00Z",
  "Bar": {
    "Prop": "2016-10-25T04:00:00Z",
    "Dates": [
      null,
      "2016-10-25T04:00:00Z"
    ]
  },
  "<Dates>": [
    "Prop1",
    "Prop2",
    "Bar.Prop",
    "Bar.Dates[1]"
  ]
}
Comments