0

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.

Community
  • 1
  • 1
Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137
  • Maybe a bit of [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) here. Json.NET can emit dates in many formats, see [here](http://www.newtonsoft.com/json/help/html/datesinjson.htm). How would you like the JSON for a `DateTIme` property to look without needing the additional `""` property? – dbc Oct 25 '16 at 17:26
  • @dbc I prefer it to be `Date`. But I don't want to depart from ISO 8601. There is no special dedicated format for `Date`. We can distinguish strings, numbers, arrays, and objects, but dates are formatted as strings. – Pavel Voronin Oct 25 '16 at 17:40

1 Answers1

1

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]"
  ]
}
dbc
  • 104,963
  • 20
  • 228
  • 340