3

I'm trying unsuccessfully to create custom DateTime converter. The problem: I have many objects to serialize, some of the containing property of DateTime with DateTime.MinValue in it. I want to serialize it as null. But all the solution that I foud asking to decorate the proper inside the object (I can't do it) Other solution that I found below, is to create converter, As far as I can understand, this convertor works only DateTime object returned explicitly and not inside other object. Please help.

public class DateTimeConverter : JsonConverter
{
    private readonly Type[] types;

    public DateTimeConverter(params Type[] types)
    {
        this.types = types;
    }

    public override bool CanConvert(Type objectType)
    {
        return types.Any(t => t == objectType);
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JToken t = JToken.FromObject(value);

        if (t.Type != JTokenType.Object)
        {
            if (value is DateTime && value.Equals(DateTime.MinValue))
            {
                t = JToken.FromObject(null);
                t.WriteTo(writer);
            }
            else
            {
                t.WriteTo(writer);
            }
        }
        else
        {
            if (value.Equals(DateTime.MinValue)) {
                t = JToken.FromObject(null);
                t.WriteTo(writer);
            }
            else {
                t.WriteTo(writer);
            }
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }
0

3 Answers 3

5

I think you are overcomplicating things. Your converter should only worry about converting date values, not objects that may contain date values. The serializer will call your converter naturally whenever a date is encountered inside any other object.

Here is a simple converter that will do what you want:

public class MinDateToNullConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        // This converter handles date values directly
        return (objectType == typeof(DateTime));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // The CanConvert method guarantees the value will be a DateTime
        DateTime date = (DateTime)value;
        if (date == DateTime.MinValue)
        {
            writer.WriteNull();
        }
        else
        {
            writer.WriteValue(date);
        }
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Here is a demo showing that the converter works on dates throughout a nested hierarchy of objects:

class Program
{
    static void Main(string[] args)
    {
        Example example = new Example
        {
            Date1 = new DateTime(2014, 2, 2),
            Date2 = DateTime.MinValue,
            Inner = new Inner
            {
                DateA = DateTime.MinValue,
                DateB = new DateTime(1954, 1, 26)
            },
            MoreDates = new List<DateTime>
            {
                new DateTime(1971, 11, 15),
                DateTime.MinValue
            }
        };

        // Set up the serializer to use our date converter
        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.Converters.Add(new MinDateToNullConverter());
        settings.Formatting = Formatting.Indented;

        string json = JsonConvert.SerializeObject(example, settings);
        Console.WriteLine(json);
    }
}

class Example
{
    public DateTime Date1 { get; set; }
    public DateTime Date2 { get; set; }
    public Inner Inner { get; set; }
    public List<DateTime> MoreDates { get; set; }
}

class Inner
{
    public DateTime DateA { get; set; }
    public DateTime DateB { get; set; }
}

Output:

{
  "Date1": "2014-02-02T00:00:00",
  "Date2": null,
  "Inner": {
    "DateA": null,
    "DateB": "1954-01-26T00:00:00"
  },
  "MoreDates": [
    "1971-11-15T00:00:00",
    null
  ]
}
Sign up to request clarification or add additional context in comments.

Comments

1

When converter implements CanConvert (and yours does), you can add it to serializer settings:

JsonConvert.SerializeObject(foo, new JsonSerializerSettings {
    Converters = {
        new DateTimeConverter()
    }
});

This way, it will be applied to all objects it supports.

1 Comment

Your example does not contain enough information to reproduce the problem. See sscce.org. "Doesn't work" provides no information either. Is converter not being called? Does converter produce wrong values? Is produced JSON incorrect? What?
0

Found the answer to my question. Actually I had 2 converters in JsonSerializer settings. I noticed that when I removed the converter on object A which contains DateTime MinValue property, My serializer works fine. This led me understand that the converter of DateTime to null is not working under other converter. So what I did is converting manually inside object A converter, not exactly clean solution, and I would prefer that the DateTimeToNull Converter will also work inside A converter. Here is what I have done inside A converter:

    public class AConverter : JsonConverter
{

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(A));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JToken jToken = JToken.FromObject(value);
        JObject jObject = (JObject)jToken;
        A aInfo = (A) value;
        string statusString = aInfo.Status == null ? string.Empty : SomeUtil.GetStateString((State)aInfo.Status.State, aInfo.Status.Downloading);
        jObject.AddFirst(new JProperty("MachineState", statusString));

        if (aInfo.UploadTime == DateTime.MinValue) {
            jObject.Remove("UploadTime");
            jObject.Add(new JProperty("UploadTime", null));
        }
        jObject.WriteTo(writer);
    }

    public override bool CanRead
    {
        get
        {
            return false;
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("It's OK!");
    }


}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.