Search code examples
c#serializationjson.netjsonconvert

Member class not serializing the defined way


How to make sure that custom class that's inside another class gets serialized as it's defined?

I have a class:

public class Event
{
    public string Type { get; set; }
    public DateTime Timestamp { get; set; }

    public override string ToString()
    {
        return JsonConvert.SerializeObject(this, Utils.IsoDateTimeConverter());
    }
}

The IsoDateTimeConverter() is defined like this:

public static class Utils
{
    private static IsoDateTimeConverter isoDateTimeConverter = null;

    public static IsoDateTimeConverter IsoDateTimeConverter()
    {
        if (isoDateTimeConverter == null)
        {
            isoDateTimeConverter = new IsoDateTimeConverter
            {
                DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
            };
        }

        return isoDateTimeConverter;
    }
}

If I call .ToString() on Event object, the item serializes correctly (the TimeStamp is in the correct format, the one I defined):

var testEvent = new Event() { Type = "Test", Timestamp = new DateTime(2021, 7, 12, 11, 6, 36, 0) };
var result = testEvent.ToString();

The result

{
   "Type":"Test",
   "Timestamp":"2021-07-12T11:06:36.0000000Z"
}

If I create a wrapper class UserEvent, around Event, and try to serialize it, the Event class is serialized differently:

public class UserEvent
{
    public string UserId { get; set; }
    public Event Event { get; set; }

    public override string ToString()
    {
        return JsonConvert.SerializeObject(this);
    }
}
// Example of usage:
var testEvent = new Event() { Type = "Test", Timestamp = new DateTime(2021, 7, 12, 11, 6, 36, 0);
var testUserEvent = new UserEvent(){UserId = "TestId", Event = testEvent};
var result = testUserEvent.ToString();

The result

{
   "UserId":"TestUserId",
   "Event":{
      "Type":"Test",
      "Timestamp":"2021-07-12T11:06:36"
   }
}    

Note that formatting of Timestamp is different than before.

How to make sure that when Event is inside another class it gets serialized correctly?

Edit 1: I know that the output will be as expected if I serialize UserEvent as JsonConvert.SerializeObject(this, Utils.IsoDateTimeConverter()) but I don't want to do that, as I don't want the wrapper class to know how the inner class looks like and how it should be serialized. The inner class should be serialized the way it's defined inside it.


Solution

  • I would suggest to create a custom IsoDateTimeConverter class where you overwrite the DateTimeFormat:

    public class CustomIsoDateTimeConverter: IsoDateTimeConverter 
    {
        public CustomIsoDateTimeConverter()
        {
           DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffffffZ";
        }
    }
    

    You can use this class without a factory method (like IsoDateTimeConverter())

    You can use the JsonConverterAttribute to decorate your Timestamp property:

    public class Event
    {
        public string Type { get; set; }
        [JsonConverter(typeof(CustomIsoDateTimeConverter))]
        public DateTime Timestamp { get; set; }
    }
    

    I would suggest to remove the ToString overrides from both classes. You might need to serialize the same data in a different format then you will be in a trouble.

    var testEvent = new Event() { Type = "Test", Timestamp = new DateTime(2021, 7, 12, 11, 6, 36, 0) };
    var testUserEvent = new UserEvent(){UserId = "TestId", Event = testEvent};
    var result = JsonConvert.SerializeObject(testUserEvent);
    

    The result

    {
       "UserId":"TestId",
       "Event":{
          "Type":"Test",
          "Timestamp":"2021-07-12T11:06:36.0000000Z"
       }
    }