使用通用解码使用所有 nullary 构造函数解析数据类型
Parsing data types with all nullary constructors using generic decode
我有以下代码:
{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}
import Data.Aeson
import GHC.Generics
data CharClass = Fighter | Rogue | Wizard deriving (Generic, Show)
instance FromJSON CharClass
instance ToJSON CharClass
我可以对这种类型的值进行编码:
*Main> encode Fighter
"\"Fighter\""
但是往返不起作用:
*Main> eitherDecode $ encode Fighter
Left "Failed reading: satisfy"
*Main> :t eitherDecode $ encode Fighter
eitherDecode $ encode Fighter :: FromJSON a => Either String a
看起来很像,但是添加预期类型不起作用:
*Main> eitherDecode $ encode Fighter :: Either String CharClass
Left "Failed reading: satisfy"
有趣的是,它确实适用于 fromJSON
/toJSON
:
*Main> fromJSON $ toJSON Fighter :: Result CharClass
Success Fighter
使至少一个构造函数为非零也可以使事情正常进行,就像我这样做一样:
data CharClass = Fighter Int | Rogue | Wizard deriving (Generic, Show)
然后再次尝试往返:
*Main> decode $ encode (Fighter 0) :: Maybe CharClass
Just (Fighter 0)
我确信我遗漏了一些简单的东西,但试图通过通用代码来追踪它让我头晕目眩。
JSON 本质上是键值对的集合,其中值可以是一些基本类型或另一个键值对集合。 Nullary 类型并不完全符合 JSON 实体本身的整个想法。但是,将它们放置在与 JSON 概念相吻合的其他类型中时,它们可以正常工作。
data F = F { a :: CharClass, b :: CharClass }
deriving (Generic, Show)
instance FromJSON F
instance ToJSON F
这看起来更像是为 JSON 设计的那种键值集合。
*Main> let x = F Fighter Rogue
*Main> x
F {a = Fighter, b = Rogue}
*Main> decode $ encode x :: Maybe F
Just (F {a = Fighter, b = Rogue})
我机器上stack
安装的aeson版本是0.8系列的,在aeson 0.8或更早的版本中,only objects and arrays were parsed at the root level.
在 aeson 0.9 中,decode
使用 value
解析器。因此,顶级的可空对象(表示为字符串)将起作用。
对于 0.8,以下示例有效。不知道为什么decodeWith
没有暴露
{-# LANGUAGE DeriveGeneric #-}
import Control.Applicative
import GHC.Generics
import Data.Aeson
import Data.Aeson.Parser
import Data.ByteString.Lazy as L
import Data.Attoparsec.ByteString.Char8 (endOfInput, skipSpace)
import qualified Data.Attoparsec.Lazy as L
data CharClass = Fighter | Rogue | Wizard deriving (Generic, Show)
instance ToJSON CharClass
instance FromJSON CharClass
decodeWith p to s =
case L.parse p s of
L.Done _ v -> case to v of
Success a -> Just a
_ -> Nothing
_ -> Nothing
{-# INLINE decodeWith #-}
valueEOF = value <* skipSpace <* endOfInput
decodeValue :: (FromJSON a) => L.ByteString -> Maybe a
decodeValue = decodeWith valueEOF fromJSON
main :: IO ()
main = print (decodeValue (encode Fighter) :: Maybe CharClass)
我有以下代码:
{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}
import Data.Aeson
import GHC.Generics
data CharClass = Fighter | Rogue | Wizard deriving (Generic, Show)
instance FromJSON CharClass
instance ToJSON CharClass
我可以对这种类型的值进行编码:
*Main> encode Fighter
"\"Fighter\""
但是往返不起作用:
*Main> eitherDecode $ encode Fighter
Left "Failed reading: satisfy"
*Main> :t eitherDecode $ encode Fighter
eitherDecode $ encode Fighter :: FromJSON a => Either String a
看起来很像
*Main> eitherDecode $ encode Fighter :: Either String CharClass
Left "Failed reading: satisfy"
有趣的是,它确实适用于 fromJSON
/toJSON
:
*Main> fromJSON $ toJSON Fighter :: Result CharClass
Success Fighter
使至少一个构造函数为非零也可以使事情正常进行,就像我这样做一样:
data CharClass = Fighter Int | Rogue | Wizard deriving (Generic, Show)
然后再次尝试往返:
*Main> decode $ encode (Fighter 0) :: Maybe CharClass
Just (Fighter 0)
我确信我遗漏了一些简单的东西,但试图通过通用代码来追踪它让我头晕目眩。
JSON 本质上是键值对的集合,其中值可以是一些基本类型或另一个键值对集合。 Nullary 类型并不完全符合 JSON 实体本身的整个想法。但是,将它们放置在与 JSON 概念相吻合的其他类型中时,它们可以正常工作。
data F = F { a :: CharClass, b :: CharClass }
deriving (Generic, Show)
instance FromJSON F
instance ToJSON F
这看起来更像是为 JSON 设计的那种键值集合。
*Main> let x = F Fighter Rogue
*Main> x
F {a = Fighter, b = Rogue}
*Main> decode $ encode x :: Maybe F
Just (F {a = Fighter, b = Rogue})
我机器上stack
安装的aeson版本是0.8系列的,在aeson 0.8或更早的版本中,only objects and arrays were parsed at the root level.
在 aeson 0.9 中,decode
使用 value
解析器。因此,顶级的可空对象(表示为字符串)将起作用。
对于 0.8,以下示例有效。不知道为什么decodeWith
没有暴露
{-# LANGUAGE DeriveGeneric #-}
import Control.Applicative
import GHC.Generics
import Data.Aeson
import Data.Aeson.Parser
import Data.ByteString.Lazy as L
import Data.Attoparsec.ByteString.Char8 (endOfInput, skipSpace)
import qualified Data.Attoparsec.Lazy as L
data CharClass = Fighter | Rogue | Wizard deriving (Generic, Show)
instance ToJSON CharClass
instance FromJSON CharClass
decodeWith p to s =
case L.parse p s of
L.Done _ v -> case to v of
Success a -> Just a
_ -> Nothing
_ -> Nothing
{-# INLINE decodeWith #-}
valueEOF = value <* skipSpace <* endOfInput
decodeValue :: (FromJSON a) => L.ByteString -> Maybe a
decodeValue = decodeWith valueEOF fromJSON
main :: IO ()
main = print (decodeValue (encode Fighter) :: Maybe CharClass)