James Ko
James Ko

Reputation: 34499

Json.NET: Converting objects to DateTimeOffsets?

I'm making requests to an API service that returns dates as JSON in the following form:

{
     "dateCreated": {
        "date":8,
        "day":4,
        "hours":22,
        "minutes":44,
        "month":10,
        "nanos":241000000,
        "seconds":46,
        "time":1194590686241,
        "timezoneOffset":480,
        "year":107
     }
}

Here time is the number of milliseconds since the Unix epoch, so that's really the only property I want to pay attention to.

Would it be possible to write a custom Json.NET converter that deserializes this to something like

public class ApiResponse
{
    public ApiResponse(DateTimeOffset dateCreated)
    {
        DateCreated = dateCreated;
    }

    public DateTimeOffset DateCreated { get; }
}

(The converter would call DateTimeOffset.FromUnixTimeMilliseconds on the object's time property.)

Here is the code I have so far:

public class ApiDateConverter : JsonConverter
{
    public override bool CanWrite => false;

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // What type is existingValue here? How do I get existingValue.time?
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
}

Upvotes: 0

Views: 655

Answers (2)

Aleksey L.
Aleksey L.

Reputation: 37918

Option 1: Your ApiDateConverter can be completed by:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        dynamic jObject = JObject.Load(reader);

        return DateTimeOffset.FromUnixTimeMilliseconds(jObject.time.Value);
    }

Option 2: Without using custom converter:

public class DateSerialized
{
    public int date { get; set; }
    public int day { get; set; }
    public int hours { get; set; }
    public int minutes { get; set; }
    public int month { get; set; }
    public int nanos { get; set; }
    public int seconds { get; set; }
    public long time { get; set; }
    public int timezoneOffset { get; set; }
    public int year { get; set; }
}

public class ApiResponse
{
    [JsonIgnore]
    public DateTimeOffset DateCreated => DateTimeOffset.FromUnixTimeMilliseconds(DateSerialized.time);

    [JsonProperty("dateCreated")]
    private DateSerialized DateSerialized { get; set; }
}

Upvotes: 1

gregsdennis
gregsdennis

Reputation: 8428

It is certainly possible to write a Json.Net converter to translate the JSON directly into your domain object, but I don't advise this.

Instead, I would suggest you create a DTO (data transfer object) that mirrors the data:

public class DateDto
{
    public long Time { get; set; }
}

Then have Json.Net deserialize into this object.

Once you have that, you can create a pure .Net converter that maps the DateDto to a DateTime or whatever structure you want (supposing maybe that you are using the NodaTime library instead of .Net's DateTime).

By doing this, you save yourself the headache of implementing custom deserialization logic, and you're following the S in SOLID: Single Responsibility Principle. You have an object whose purpose is solely to represent the data, and a converter to translate that into whatever your application uses.

It also increases the testability of your code.

Upvotes: 1

Related Questions