How to read/cast/validate JSON in PureScript?

Question: In my PureScript program, I’ve recieved a foreign JSON object, which I expect to have a particular structure. How do I safely “cast” that to a PureScript data type?

Or maybe I don’t have any expectations about the structure of the JSON, and I want to read the JSON and discover its structure?

This is a super common question, and I was using PureScript for years before I figured out what best answers were.

1. Argonaut

The decodeJson function from argonaut can infer the structure of the JSON you’re expecting from the type of the data that you want to cast it to. If the structure of the JSON doesn’t match the type, then it returns an error in Left.

show $ do
    x :: Array {a::Int,b::String} <- decodeJson =<< parseJson """[{"a":2,"b":"stuff"}]"""
    pure x

Results in (Right [{ a: 2, b: "stuff" }])

See the argonaut-codecs Quick start for more decodeJson examples:

https://pursuit.purescript.org/packages/purescript-argonaut-codecs

If you want to decodeJson for some type that doesn’t already have a DecodeJson instance, then you can write a DecodeJson instance for your type.

If you want to discover the structure of the JSON, you can write monadic parsers in the Either monad with the getField* functions. You can also preview the Json with Argonaut.Prisms.

2. Simple.JSON

The Simple.JSON.read' function can also infer the expected structure of JSON from the PureScipt data type that we are trying to read into.

https://purescript-simple-json.readthedocs.io/en/latest/intro.html

Simple.JSON also has many more features.

3. F Monad

The most powerful and general way to read foreign data is by writing monadic parsers for the F monad. You run the parser with runExcept.

Those are the best answers

I’ve seen my own colleagues and many people on the internet write libraries which duplicate the functions I’ve described here because these answers are hard to find if you’re new to PureScript and you’re not sure what you’re looking for.

The classic essay on the general problem of how to read unstructured untyped data into a typed data structure is Parse, don’t validate and I strongly recommend this essay.

4 Likes

The codec-argonaut library is used by those of us who like a less typeclass-reliant version of handling things too.

The name is unfortunate as it’s easily confused with argonaut-codecs, but quite different indeed.

6 Likes

codec-argonaut is superb. I think it should the default option when dealing with data on the boundaries, everything should be as explicit there as possible.

Many people don’t like that they should explicitly write codecs (encoders/decoders) and they want it to be derived from types (and like that purescript has such capabilities), but it is a really bad idea when dealing with boundaries, even if you are trying to have special transport types.

Writing codecs is easy and fun actually, it gives you flexibility (e.g., you may have different modifications of the codec for different transfer contexts) and a feeling of control over what is going on on the boundaries.

I would prefer it even for one-way communication with external API (though it lacks currently optional fields functionality) out-of-the-box.

1 Like

Updated How to write PureScript React to replace TypeScript React in 2021 with this new material: blog-posts/PureScript-React.md at master · xc-jp/blog-posts · GitHub

3 Likes