简体   繁体   中英

Exception when deserializing NodaTime LocalDate

I am seeing an exception when deserializing a JSON string that contains a JSON-serialized LocalDate object (see the end of this question for the JSON snippet).

This is how I am deserializing:

var settings = new JsonSerializerSettings();
settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
var output = JsonConvert.DeserializeObject<MyObject>(json, settings);

I see this exception message:

NodaTime.Utility.InvalidNodaDataException: 'Unexpected token parsing LocalDate. Expected String, got StartObject.'

This the MyObject class:

class MyObject
{
    public LocalDate Date { get; set; }
    public string AnotherProperty { get; set; }
}

Here is the JSON snippet I'm trying to deserialize:

{
    "Date": {
        "Calendar": {
            "Id": "ISO",
            "Name": "ISO",
            "MinYear": -9998,
            "MaxYear": 9999,
            "Eras": [{
                "Name": "BCE"
            }, {
                "Name": "CE"
            }]
        },
        "Year": 2017,
        "Month": 7,
        "Day": 10,
        "DayOfWeek": 1,
        "YearOfEra": 2017,
        "Era": {
            "Name": "CE"
        },
        "DayOfYear": 191
    },
    "AnotherProperty": "A string"
}

I have figured it out now - my problem was a bad assumption on my part in the ASP.NET route handler. @LB 's question actually got me thinking a little more.

I was assuming that the built-in JSON serializer was serializing LocalDate in MyObject correctly in this example:

[HttpGet("myobject")]
public MyObject GetMyObject()
{
    return new MyObject()
    {
        Date = LocalDate.FromDateTime(DateTime.Now),
        AnotherProperty = "A string"
    };
}

The result of this API would be the same as the JSON snippet in the question.

Calling SerializeObject and passing settings in, for every API handler is not a good idea either because I lose the object return type on every route handler I have.

To ensure that LocalDate gets serialized correctly in every handler, I do the following in the ConfigureServices method of the Startup class:

services.AddMvc().AddJsonOptions(options =>
{
    // NodaConverters lives in the NodaTime.Serialization.JsonNet assembly
    options.SerializerSettings.Converters.Add(NodaConverters.LocalDateConverter);
});

Now when I call the above API, LocalDate is serialized correctly, like this:

{
    "Date":"2017-07-10",
    "AnotherProperty":"A string"
}

This is the format that the DeserializeObject is also expecting.

You can use class structure like this

public class Era
{
    public string Name { get; set; }
}

public class Calendar
{
    public string Id { get; set; }
    public string Name { get; set; }
    public int MinYear { get; set; }
    public int MaxYear { get; set; }
    public List<Era> Eras { get; set; }
}

public class Era2
{
    public string Name { get; set; }
}

public class Date
{
    public Calendar Calendar { get; set; }
    public int Year { get; set; }
    public int Month { get; set; }
    public int Day { get; set; }
    public int DayOfWeek { get; set; }
    public int YearOfEra { get; set; }
    public Era2 Era { get; set; }
    public int DayOfYear { get; set; }
}

public class RootObject
{
    public Date Date { get; set; }
    public string AnotherProperty { get; set; }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM