Hacker News new | ask | show | jobs
by pyrale 3247 days ago
Do you have examples of things that don't fit the vanilla FromJSON ? From the top of my head, there's lensed things (which can easily be done once with a LensJSON typeclass), and things which use a different serialization (e.g. no "tag" field, etc) for which there is little you can do, if each of your data types has its own standard. If they do, though, you could easily define a typeclass for each source (e.g. StackoverflowJSON, HackernewsJSON, etc).
1 comments

We don't do type classes for this purpose. If you have to interface with some JSON with predefined schema, we just write the instances manually. It's not that hard at all—usually it's just calling withObject with a series of lifted function applications `Constructor <$> (o .: "field1") <*> (o .: "field2")`. Remember the Parser type is a Functor/Applicative/Monad/Alternative/MonadPlus so there's a whole host of utilities for these classes that make writing such instances both simple and concise. Of course if you are doing simple things like removing the leading underscore on lenses data typed, just use TH to derive the instance, passing slightly modified `Options`.

If you need to manually handle tags, here's a snippet that can help you:

    -- | Safely accesses a JSON object where the value at a key is text. It takes an
    -- object, a key, and a continuation of what to do when this key is present.
    --
    -- Example:
    --
    -- @
    --     data D = A | B | C Int
    --     instance FromJSON D where
    --       parseJSON = withObject "D" $ \o ->
    --         o .:=> "tag" $ \case
    --           "A" -> pure A
    --           "B" -> pure B
    --           "C" -> C <$> (o .: "theInt")
    --           t -> fail $ "Unrecognized tag in type D: " ++ unpack t
    -- @
    (.:=>) :: Object -> Text -> (Text -> Parser a) -> Parser a
    o .:=> k = \m -> o .: k >>= withText ("Object with mandatory key " <> unpack k) m