Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 解析同质多态json数组_Haskell_Aeson - Fatal编程技术网

Haskell 解析同质多态json数组

Haskell 解析同质多态json数组,haskell,aeson,Haskell,Aeson,我有一个数据类型,其中一个字段是n其他数据类型的one列表(n很小,类型是预先知道的)。我想做一个JSON解析器,但我不太明白。我曾尝试创建一个宠物类型的类,并使它们都成为它的实例,但这似乎是一条死胡同。任何帮助都将不胜感激 作为一个简单的例子,我有一个人数据类型,可以有一个宠物列表,狗或猫,但不能两者兼而有之 下面是一个例子: {-# LANGUAGE OverloadedStrings #-} import Control.Applicative import Data.Aeson imp

我有一个数据类型,其中一个字段是
n
其他数据类型的one列表(
n
很小,类型是预先知道的)。我想做一个JSON解析器,但我不太明白。我曾尝试创建一个宠物类型的类,并使它们都成为它的实例,但这似乎是一条死胡同。任何帮助都将不胜感激

作为一个简单的例子,我有一个
数据类型,可以有一个宠物列表,狗或猫,但不能两者兼而有之

下面是一个例子:

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Data.Aeson
import Data.ByteString.Lazy as L
import Data.Aeson.Types (Parser)
import Control.Monad (mzero)

data Person = Person {
  name :: String,
  species :: String,
  pets :: [?] -- a list of dogs OR cats
} deriving Show

instance FromJSON (Person a) where
  parseJSON (Object v) = ???

data Dog = Dog {
  dogField :: String
} deriving Show

 instance FromJSON Dog where
  parseJSON (Object v) = Dog <$>
    v .: "dogField"

data Cat = Cat {
  catField :: String
} deriving Show

instance FromJSON Cat where
  parseJSON (Object v) = Cat <$>
    v .: "catField"
{-#语言重载字符串}
导入控制
导入数据.Aeson
将Data.ByteString.Lazy导入为L
导入Data.Aeson.Types(解析器)
进口管制.单子(mzero)
数据人=人{
名称::字符串,
种类::字符串,
宠物::[?]——狗或猫的列表
}衍生节目
实例FromJSON(人员a),其中
parseJSON(对象v)=???
数据狗{
dogField::String
}衍生节目
实例FromJSON狗在哪里
parseJSON(对象v)=狗
v.:“多格菲尔德”
数据猫=猫{
catField::String
}衍生节目
实例FromJSON Cat where
parseJSON(对象v)=Cat
v.:“猫场”

表示一种或另一种类型的标准方法是使用
类型,例如:

data Person { ..., pets :: Either [Dog] [Cat] }
此外,您可能对使用GHC泛型自动派生to/FromJSON实例感兴趣

使用
的数据结构示例:

{-# LANGUAGE DeriveGeneric #-}

import Data.Aeson
import GHC.Generics

data Person = Person {
  name :: String,
  species :: String,
  pets :: Either [String] [Int]
} deriving (Show,Generic)

instance ToJSON Person    -- instances are auto-derived    
instance FromJSON Person

doit = do
  let me = Person "user5402" "Human" (Right [42])
  print $ encode me
如果您有两个以上的选择,您可以轻松创建自己的总和类型,如下所示:

-- assume the possible pet types are: Dog, Cat, Rodent, Reptile
data Pets = Dogs [Dog] | Cats [Cat] | Rodents [Rodent] | Reptiles [Reptile]
  deriving (Show, Generic)

data Person { ..., pets :: Pets }
  deriving (Show, Generic)

doit = do
  let me = Person "user5402" "Human" (Rodents [agerbil, amouse])
  print $ encode me

其中,
agerbil
amouse
啮齿动物的值。

我正在修改@user5402的答案,因为我不喜欢泛型添加的“标签”和“内容”字段。也接受了他的回答,因为他给了我关于如何构造求和类型的关键见解

instance FromJSON Pets where
  parseJSON (Object o) = (parsePets o "pets")
  parseJSON _ = mzero

parsePets :: Object -> T.Text -> Parser Pets
parsePets o key = case H.lookup key o of
               Nothing -> fail $ "key " ++ show key ++ " not present"
               Just v  -> parseToCatsOrDogs (o .: "species") v
{-# INLINE parsePets #-}

parseToCatsOrDogs :: Parser String -> Value -> Parser Pets
parseToCatsOrDogs speciesParser (Array v) = speciesParser >>= \species -> case species of
  "dog" -> (V.mapM (\x -> parseJSON $ x) v) >>= \ dogVector -> return $ Dogs (V.toList dogVector)
  "cat" -> (V.mapM (\x -> parseJSON $ x) v) >>= \ catVector -> return $ Cats (V.toList catVector)
  _ -> mzero
parseToCatsOrDogs _ _ = mzero

宠物不同时有猫和狗,这对你的需求来说是必要的,还是只是一个简单的假设?也许不是必要的,但它反映了我当前的数据模型。但如果没有这个假设,解决方案也会很好!如果你真的想把猫和狗分开,自然的做法是在
Person
中有不同类型的单独列表,这会起作用-我将它从postgres json字段序列化为+,如下所示well@sportanova:您将您的猫和狗描述为“n种其他数据类型中的一种(n很小,并且这些类型是预先知道的)”。在这种情况下,几乎不需要创建类,sum类型(即具有多个构造函数的类型)通常是最简单的选择
或者
是两种可能的求和类型的标准方法。user5402@duplode谢谢!最后,我使用了您答案的一个修改版本,从通用的ToJSON/FromJSON中删除了“content”和“tag”字段——见下文