我的JSON
在其中一个属性中包含一个编码的JSON
字符串:
{
"firstName": "Frederick",
"lastName": "Krueger",
"address": "{\"street\": \"Elm Street, 13\", \"city\": \"Springwood\", \"state\": \"OH\"}"
}
鉴于我有一个数据类型:
data Address = Address { street :: String, city :: String, state :: String }
deriving (Generic, Show)
data Person = Person { firstName :: String, lastName :: String, address :: Address }
deriving (Generic, Show)
如何为FromJSON
实施Person
?
答案 0 :(得分:1)
这是我尝试使用小帮手解析器:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module MyModule where
import Control.Monad
import Data.Aeson
import Data.Aeson.Types
import Data.ByteString (ByteString)
import qualified Data.Text.Encoding as Text
import GHC.Generics
data Address = Address
{ street :: String
, city :: String
, state :: String
} deriving (Generic, Show)
instance FromJSON Address
data Person = Person
{ firstName :: String
, lastName :: String
, address :: Address
} deriving (Generic, Show)
instance FromJSON Person where
parseJSON (Object o) =
Person <$> o .: "firstName" <*> o .: "lastName" <*>
(parseAddress =<< o .: "address")
parseJSON _ = mzero
parseAddress :: Value -> Parser Address
parseAddress (String s) = do
let maybeObject = decodeStrict (Text.encodeUtf8 s)
case maybeObject of
Nothing -> mzero
Just o -> parseJSON (Object o)
parseAddress _ = mzero
testString :: ByteString
testString =
"{\n \"firstName\": \"Frederick\",\n \"lastName\": \"Krueger\",\n\"address\": \"{\\\"street\\\": \\\"Elm Street, 13\\\", \\\"city\\\": \\\"Springwood\\\", \\\"state\\\": \\\"OH\\\"}\"\n}\n"
并在repl中运行它:
λ> eitherDecodeStrict testString :: Either String Person
Right (Person {firstName = "Frederick", lastName = "Krueger", address = Address {street = "Elm Street, 13", city = "Springwood", state = "OH"}})
简而言之,Person
实例使用=<<
运算符将两个解析器链接在一起 - 由o .: "address"
和parseAddress
生成的解析器。在parseAddress
内,如果我们看到String
,我们就可以检查该值并进行进一步处理。 decodeStrict
用于尝试将字符串解码为对象,parseAddress
parseJSON
可以使用Address
将该对象解析为parseAddress :: Value -> Parser Address
parseAddress (String s) =
either (const mzero) parseJSON (eitherDecodeStrict (Text.encodeUtf8 s))
parseAddress _ = mzero
。
或者作为没有中间值的单线条:
vertical-align
答案 1 :(得分:0)
这样做的一种方法是使用函数withObject
编写自己的解析器。在该功能中,您明确处理您的地址案例。例如:
parsePerson :: Value -> Parser Person
parsePerson = withObject "expected person"
(\obj -> do
fname <- obj .: "firstName"
lname <- obj .: "lastName"
(addr :: String) <- obj .: "address"
let addr' = decode (BS.fromStrict $ pack addr)
return $ Person { firstName = fname, lastName = lname, address = fromJust addr' })
instance FromJSON Address where
parseJSON (Object v) = Address <$>
v .: "street" <*>
v .: "city" <*>
v .: "state"
parseJSON _ = empty
这些是我必须做的必要的导入:
import Data.Aeson
import Data.Aeson.Types (Parser(..), parseEither)
import Data.Maybe (fromJust)
import Data.Text (Text)
import Data.ByteString.Char8 (pack)
import qualified Data.ByteString.Lazy as BS
请注意,在您的代码中,您可能希望避免使用部分函数fromJust
。将JSON字符串存储在名为add.json
:
main :: IO ()
main = do
dat <- BS.readFile "./add.json"
let val = decode dat :: Maybe Value
print $ (parseEither parsePerson (fromJust val))
执行时输出:
sibi::jane { ~/scripts }-> ./aeson.hs
Right (Person {firstName = "Frederick", lastName = "Krueger", address = Address {street = "Elm Street, 13", city = "Springwood", state = "OH"}})