Write a simple custom JSON decoder for Decco in ReasonML

Murphy Randle
InstructorMurphy Randle

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 3 years ago

Decco is a great library for automatically generating JSON encoders and decoders, but most real-world projects will quickly run into the need to add custom data types that Decco doesn't ship with support for. This lesson shows how to simply add a custom data type by implementing a codec that reads and writes Javascript dates as floats.

Instructor: [0:00] Here I've got a simple function that takes some JSON data and prints it out. Let's see what it looks like when I run it. Here you could see that my JSON is an object with a date field, and that date field contains the number of milliseconds since 1970.

[0:13] This is one format of representing dates in JavaScript, but what we want to do really, is convert this into a date object at runtime, so we can use all the nice date functions with it. Let's write a decoder, type data = {date: Js.Date.t}.

[0:30] I'm going to use a handy language extension called Decco. [@decco] to generate a JSON Decoder for me. Uh-oh, you'll see that I have a compiler error here. It says "Unbound value Js.Date.t_encode," and that's because Decco doesn't have built-in support for JS date datatype.

[0:49] The error message isn't amazing, but I know the way Decco works. Decco looks for a function called t_encode inside of the module for the type that it's trying to encode or Deccode. I'm going to go ahead and add a custom decoder to handle the date type.

[1:04] We'll do that by making a module DateAsFloat=. Then inside of here, let's make a type alias, type t = Js.Date.t. Now the things we need in this custom codec are an encoding function and a decoding function.

[1:20] The decoding function takes JSON and returns the type you need, and encoding take the type you need and returns JSON. Let t_decode = and we're going to take in JSON, and then do something with it. Let's use some handy functions that Decco includes. Decco.floatFromJson(json).

[1:40] This produces a result type, you could see that it doesn't just produce a float, it returns a result saying that it either succeeded or failed. We can map over that result using belt, Belt.Result.map(), and now we can turn that float into something else. Let's use Js.Date.fromFloat.

[1:57] Quickly enough, we've made a function that takes in JSON and returns a result of date on the left-hand side, or decoder error on the right-hand side. That just happens to be the exact signature that Decco is looking for when it's trying to generate a codec.

[2:09] Now let's do the encoder function. Let t_encode = and the type we're taking in is a date. Now the first thing we need to do is convert that date to a float again. date goes into Js.Date.getTime, that returns the number of milliseconds since 1970, and then we need to turn it into JSON.

[2:29] We'll use Decco again, Decco.floatToJson. Now if we look at the type signature for this, we've got a function that takes a date and returns JSON. Now instead of Js.Date.t, let's use DateAsFloat.t, you see our compiler error went away.

[2:48] Now let's actually use this. I'll add a switch statement here, and I'm going to call the data_decode function, which is the function Decco generates for us, and I'll pass in the JSON. We should get out OK(data), meaning it succeeded and we can log that, Js..log2("data"), or if it's an error, we can log that to Error(msg) into Js.log("failed"), but we know it's going to succeed, because the type system says so, and we know what our data looks like beforehand.

[3:17] Now to see what it looks like when we encode data, let's go ahead and log to the console new data, some data that we're going to make, and then encode. We'll use data_encode, and then we've got to make an object with a date field, and let's throw in our own Js.Date, and let's move this JSON log up to the top.

[3:39] First, we'll see the JSON logged, then we'll see the decoded JSON log, and then we'll see the result of taking some new data and encoding it back to the console. Now you see we've got JSON at the top which is the raw object with the float in it.

[3:53] We've got the decoded data which has a date field and an actual JasvaScript object, date object, you can see it's parsed it and represented here as an ISO string. Then we've reencoded the data again, making a Js.Date object, and it's encoded it as a JavaScript object with a float on it.