Member class not serializing the defined way

Viewed 83

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.

1 Answers

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"
   }
}
Related