Aeson:如何解析具有字符串化对象元素的对象?

Aeson: how do I parse an object with an element that is a stringified object?

我需要解析一个包含字符串元素的对象,其中字符串本身是一个字符串化对象:

{ 
  "a"   : "apples",
  "bar" : "{\"b\":\"bananas\"}"
}

我想将其解析为 Just ( Foo { fooA = "apples", fooBar = Bar { barB = "bananas" } } ) 所以如果解析 bar returns Nothing 然后解析整个对象 returns Nothing,即结果好像对象的 bar 元素没有被字符串化。

这是我尝试解析 testData returns Nothing:

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Data.Aeson  
import Data.Aeson.Types

data Foo = Foo { fooA :: String
               , fooBar :: Bar
               } deriving (Show)

instance FromJSON Foo where
  parseJSON (Object o) = do bar <- (o .: "bar")
                            Foo <$> o .: "a" <*> parseJSON bar
  parseJSON x          = typeMismatch "Foo" x

data Bar = Bar { barB :: String
               } deriving (Show)

instance FromJSON Bar where
  parseJSON (Object o) = Bar <$> o .: "b"
  parseJSON x          = typeMismatch "Bar" x

testData = "{ \"a\":\"apples\", \"bar\":\"{\\"b\\":\\"bananas\\"}\" }"

main :: IO ()
main = putStrLn $ show d
  where d :: Maybe Foo
        d = decode testData

如何修改上面的代码以更接近我的需要?

您可以通过以下方式更深入地了解正在发生的事情:

main = print (eitherDecode testData :: Either String Foo)

显示:Left "Error in $: expected Bar, encountered String"

在此代码中:

 parseJSON (Object o) = do bar <- (o .: "bar")
                           Foo <$> o .: "a" <*> parseJSON bar

barString ... 值。

要完成你想做的事情,你可以向 Bar 的 FromJSON 实例添加一个 case 来捕捉这个:

instance FromJSON Bar where
  ...
  parseJSON (String text) =
    case eitherDecode (textToLBS text) of
      Left  e -> fail $ "while decoding a Bar: " ++ e
      Right b -> return b
  ...

或者您可以将此代码放入 Foo 的 parseJSON 定义中。

此处textToLBS将严格的文本转换为惰性字节字符串:

import qualified Data.Text as T
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text.Encoding as TE

textToLBS :: T.Text -> LBS.ByteString
textToLBS t = LBS.fromStrict (TE.encodeUtf8 t)

代码位于:http://lpaste.net/143183