4

Consider the following class:

public class Person
{
    [DisplayFormat(DataFormatString = "dd/MM/yyyy")]
    public DateTime DOB { get; set; }
}

When serializing the object to JSON with Json.NET I would like to override the serializing if the property is decorated with a DisplayFormatAttribute. This should effectively call:

object toBeSerialized = DOB.ToString(attribute.DateFormatString)

Effectively returning a formatted string instead of DateTime.

What's the recommended approach for achieving this with Json.NET?

Ben Foster
  • 34,340
  • 40
  • 176
  • 285
  • Short of writing a custom JsonConverter for each of your format, and decorating your properties with these JsonConverter, no way. However, this leads to the question: shouldn't the format handling be done in the UI layer? – yorah Apr 04 '14 at 10:10
  • Actually, there seems to be a way to do it without writing custom converters, see below – yorah Apr 04 '14 at 10:36
  • I meant: by writing one custom converter, and assigning it to the properties through a contract resolver. – yorah Apr 04 '14 at 13:38
  • See also: [Specifying a custom DateTime format when serializing with Json.Net](http://stackoverflow.com/q/18635599/10263) – Brian Rogers Apr 04 '14 at 15:10

1 Answers1

2

The JsonConverter class does not have access to the property declaration, so it has no way to get the attributes it is decorated with.

Also, there is no way to decorate your property with a custom JsonConverter, and pass it the date format you want to use, because you are supposed to pass the type of the JsonConverter you want to use, and not the instance:

// This doesn't help us...
[JsonConverter(typeof(CustomDateTimeFormatConverter)]
public DateTime DOB { get; set; }

So how can we do ?

In order to pass a format string to our Converter, we can associate it through the following ContractResolver (which has access to the property info through reflection):

public class CustomDateTimeFormatResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        // skip if the property is not a DateTime
        if (property.PropertyType != typeof (DateTime))
        {
            return property;
        }

        var attr = (DisplayFormatAttribute)member.GetCustomAttributes(typeof(DisplayFormatAttribute), true).SingleOrDefault();
        if (attr != null)
        {
            var converter = new IsoDateTimeConverter();
            converter.DateTimeFormat = attr.DataFormatString;
            property.Converter = converter;
        }

        return property;
    }
}

This will associate an IsoDateTimeConverter converter with our custom DateTimeFormat to our datetime at runtime, using the format specified in the DisplayFormat attribute.

And in order to tell Json.NET to use our ContractResolver, we need to use this syntax when serializing to json:

string json = JsonConvert.SerializeObject(
            p, Formatting.Indented,
            new JsonSerializerSettings
                { ContractResolver = new CustomDateTimeFormatResolver() });
yorah
  • 2,653
  • 14
  • 24
  • 5
    You don't need a custom converter here; inside your resolver just use the `IsoDateTimeConverter` and set the `DateTimeFormat` on it. – Brian Rogers Apr 04 '14 at 15:15