Search code examples
haskellaeson

Parsing embedded JSON with Aeson


I'm trying to parse embedded JSON of the form

{
  "foo":"bar",
  "baz":"\{\"somekey\":\"someval\"\}"
}

with Aeson in Haskell. Here are my types:

data BaseType = BaseType { foo :: String, baz :: EmbeddedType } deriving(Show)

instance FromJSON BaseType where
  parseJSON = withObject "BaseType" $ \o -> do
    foo <- o .: "foo"
    baz <- o .: "baz"
    return $ BaseType { foo=foo, baz=baz }

data EmbeddedType = EmbeddedType { somekey :: String }

instance FromJSON EmbeddedType where
  parseJSON = withObject "EmbeddedType" $ \o -> do
    somekey <- o .: "somekey"
    return $ EmbeddedType {somekey=somekey}

Obviously, the FromJSON instance for BaseType doesn't work, since it sees it as a Value String instead of as more JSON for it to parse. I tried to find a way to use decodeEither in my FromJSON BaseType instance, but that required that I do some black magic to convert from String to ByteString, and I feel like there must be a neater way, possibly related to withEmbeddedJSON.

How can I make this work correctly?


Solution

  • I guess it would be something like this (untested):

    instance FromJSON BaseType where
      parseJSON = withObject "BaseType" $ \o -> do
        foo <- o .: "foo"
        bazText <- o .: "baz"
        baz <- withEmbeddedJSON "EmbeddedType" parseJSON (String bazText)
        return $ BaseType { foo=foo, baz=baz }
    

    Or you could consider moving the call to withEmbeddedJSON into the EmbeddedType instance; then o .: "baz" should Just Work in the BaseType instance, at the cost of no longer having a handle onto a parser that just does EmbeddedType parsing without de-stringifying:

    instance FromJSON BaseType where
      parseJSON = withObject "BaseType" $ \o -> do
        foo <- o .: "foo"
        baz <- o .: "baz"
        return $ BaseType { foo=foo, baz=baz }
    
    instance FromJSON EmbeddedType where
      parseJSON = withEmbeddedJSON "EmbeddedType" . withObject "EmbeddedType" $ \o -> do
        somekey <- o .: "somekey"
        return $ EmbeddedType {somekey=somekey}