用 Aeson 解析嵌入式 JSON

Parsing embedded JSON with Aeson

我正在尝试解析嵌入的 JSON 形式

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

与 Aeson 一起 Haskell。这是我的类型:

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}

显然,BaseTypeFromJSON 实例不起作用,因为它将它视为 Value String 而不是更多 JSON 来解析。我试图找到一种在我的 FromJSON BaseType 实例中使用 decodeEither 的方法,但这需要我做一些黑魔法才能从 String 转换为 ByteString,我觉得一定有更简洁的方法,可能与 withEmbeddedJSON.

有关

我怎样才能使它正常工作?

我想应该是这样的(未经测试):

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 }

或者您可以考虑将对 withEmbeddedJSON 的调用移动到 EmbeddedType 实例中;那么 o .: "baz" 应该只在 BaseType 实例中工作,代价是不再有一个只进行 EmbeddedType 解析而不去字符串化的解析器的句柄:

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}