简体   繁体   English

使用通用解码解析所有nullary构造函数的数据类型

[英]Parsing data types with all nullary constructors using generic decode

I have the following code: 我有以下代码:

{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}

import Data.Aeson
import GHC.Generics

data CharClass = Fighter | Rogue | Wizard deriving (Generic, Show)

instance FromJSON CharClass
instance ToJSON CharClass

I can encode values of this type: 我可以编码这种类型的值:

*Main> encode Fighter
"\"Fighter\""

But round-tripping doesn't work: 但是往返不起作用:

*Main> eitherDecode $ encode Fighter
Left "Failed reading: satisfy"
*Main> :t eitherDecode $ encode Fighter
eitherDecode $ encode Fighter :: FromJSON a => Either String a

It looks a lot like this answered question , but adding the expected type doesn't work: 它看起来很像这个回答的问题 ,但添加预期的类型不起作用:

*Main> eitherDecode $ encode Fighter :: Either String CharClass 
Left "Failed reading: satisfy"

Interestingly, it does work for fromJSON / toJSON : 有趣的是,它确实适用于fromJSON / toJSON

*Main> fromJSON $ toJSON Fighter :: Result CharClass
Success Fighter

Making at least one of the constructors non-nullary also makes things work, like if I do this: 使至少一个构造函数非nullary也使事情有效,就像我这样做:

data CharClass = Fighter Int | Rogue | Wizard deriving (Generic, Show)

And then try to round-trip again: 然后尝试再次往返:

*Main> decode $ encode (Fighter 0) :: Maybe CharClass
Just (Fighter 0)

I'm sure I'm missing something simple, but attempting to trace this through the generic code made my head spin. 我确定我错过了一些简单的东西,但试图通过通用代码追踪这一点让我头晕目眩。

JSON is fundamentally a collection of key-value pairs, where values can be a few primitive types or another collection of key-value pairs. JSON基本上是键值对的集合,其中值可以是一些基本类型或另一组键值对。 Nullary types just don't fit in very well with the whole idea of being JSON entities by themselves. Nullary类型本身并不适合作为JSON实体的整体想法。 However, they work fine when placed within other types that mesh well with the JSON concept. 但是,当它们放置在与JSON概念很好地融合的其他类型中时,它们可以正常工作。

data F = F { a :: CharClass, b :: CharClass }
    deriving (Generic, Show)

instance FromJSON F
instance ToJSON F

This looks more like the sort of key-value collection JSON was designed for. 这看起来更像是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或更早版本中, 只有对象和数组在根级别进行了解析

In aeson 0.9, decode uses value parser. 在aeson 0.9中, decode使用value解析器。 So nullable object (represented as string) at top-level will work. 因此,顶级的可空对象(表示为字符串)将起作用。

For 0.8 the below example works. 对于0.8,以下示例有效。 I don't know why decodeWith isn't exposed. 我不知道为什么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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM