Custom JSON converter for Noda Time

I'm developing for .NET Core, so I'm using the unstable latest alpha of Noda Time (2.0.0-alpha20160729). Data is being handled with JSON.

I will be accepting user input for a date of birth field, and I want to support multiple input formats in case the user can't follow directions gets confused. For example, I want to accept both MM-dd-yyyy and M/d/yy, among others.

The serialization docs specifically state the following:

Custom converters can be created easily from patterns using NodaPatternConverter.

I cannot for the life of me figure out how to do this though. From what I understand, I will need to implement both NodaPatternConverter<LocalDate> itself, IPattern<LocalDate> for parsing, and Action<T> for validation.

I started writing IPattern.Parse<LocalDate>, but that function returns ParseResult<LocalDate> which is apparently inaccessible. It cannot be instantiated or used in any way that I have found. I'm therefore stuck.

How do I properly create a custom JSON converter for Noda Time?

Jon Skeet
people
quotationmark

Firstly, this doesn't sound like the right thing to do in a JSON converter. If you're accepting user input directly in your JSON, that should be treated as a string, and parsed later, IMO. JSON is a machine-to-machine format, not a human-to-machine format. Assuming this is a web app, you might want to use moment.js to parse the data at the client and reformat it as ISO-8601. Alternatively, deserialize it as a string and then convert it in your server-side code.

Anyway, for a JSON converter you only need to implement IPattern<LocalDate> - you don't need to implement NodaPatternConverter<LocalDate> as that already exists. You just need:

var pattern = ...;
var converter = new NodaPatternConverter<LocalDate>(pattern);

Now, to implement your pattern, you probably want to actually create it out of existing patterns - write an implementation which delegates to one IPattern<LocalDate> after another until the result is a ParseResult<T> which is successful - or return the final unsuccessful ParseResult<T>. Note that ParseResult<T> isn't inaccessible - but you can't (currently) create your own instance of it. That's something I should probably address, but in this case you don't really need to.

The code you need already exists but isn't exposed - you want the Parse part of CompositePattern. To implement the Format part, you could just use the first of your patterns to format the value... if you even need to.

people

See more on this question at Stackoverflow