Haskell 如何使用Aeson解析分布在数组中的值?
我的json值为:Haskell 如何使用Aeson解析分布在数组中的值?,haskell,aeson,Haskell,Aeson,我的json值为: { "name": "xyz1", "extra": [ { "this_string_A": "Hello" }, { "this_string_B": "World" } ] } 以及以下数据类型: data Abc = Abc { name :: String
{
"name": "xyz1",
"extra": [
{
"this_string_A": "Hello"
},
{
"this_string_B": "World"
}
]
}
以及以下数据类型:
data Abc = Abc
{ name :: String
, a :: Maybe String
, b :: Maybe String
} deriving (Generic, Show)
在上面的例子中,我希望它解析为Abc“xyz1”(只是“Hello”)(只是“World”)
我不知道如何在aeson解析器
上下文中有条件地解析extra
(它是一个JSON数组)中的值。如何获取额外[0]的。例如,此字符串\u a
?我
我的尝试:
我想我可以创建自己的解析器(可能是字符串)
函数,但遇到了令人困惑的错误:
instance FromJSON Abc where
parseJSON = withObject "Abc" $ \v -> Abc
<$> v .: "name"
<*> myParse v
<*> myParse v
myParse :: Object -> Parser (Maybe String)
myParse x = withArray "extra" myParse2 (x)
myParse2 :: Array -> Parser (Maybe String)
myParse2 = undefined
如果我将x
替换为Object x
,则得到解析错误:
Left "Error in $: parsing extra failed, expected Array, but encountered Object"
完整示例(运行
test
函数进行测试):
{-#派生通用语言}
{-#语言重载字符串}
模块示例,其中
进口GHC.仿制药
导入数据.Aeson
导入Data.Aeson.Types
数据Abc=Abc
{name::String
,a::可能是字符串
可能是字符串
}派生(通用、显示)
来自JSON Abc的实例,其中
parseJSON=withObject“Abc”$\v->Abc
v.:“姓名”
(v.:“extra”)--查找对象在何处具有此字符串的键??
(v.:“extra”)--查找对象在何处具有此字符串的键??
测试::字符串Abc或
测试=解码示例JSON
exampleJson=“{\'name\':\'xyz1\',\'extra\':[{\'this\u string\u A\':\'Hello\',{\'this\u string\u B\':\'World\'}”
部分答案
instance FromJSON Abc where
parseJSON = withObject "Abc" $ \v -> Abc
<$> v .: "name"
<*> (v .: "extra" >>= myParse)
<*> (v .: "extra" >>= myParse)
myParse :: Array -> Parser (Maybe String)
myParse x = withArray "extra" (lookupDictArray "this_string_a") (Array x)
lookupDictArray :: Text -> Array -> Parser (Maybe String)
lookupDictArray k a = do
let v = Vector.find (maybe False (HashMap.member k) . parseMaybe parseJSON) a
case v of
Just v' -> withObject "grrrrrrrrrrr" (\v -> v .: k) v'
Nothing -> pure Nothing
xxx的“helpers”让一切都有点尴尬,但这就是原因
Aeson解析器
类型命名错误,这会导致混淆。
AesonParser
对象的思想是它们表示一元解析结果。(这不同于您在Parsec等中找到的表示实际一元语法分析器的语法分析器
对象。)因此,您应该将语法分析器a
视为与或ParseError a
同构的一元结果,这是一种可能失败的结果
这些解析结果通常以应用程序的方式组合在一起。因此,如果您有一个类似以下的解析器:
data Xyz = Xyz { x :: String, y :: String }
instance FromJSON Xyz where
parseJSON = withObject "Xyz" $ \v ->
Xyz <$> v .: "x" <*> v .: "y"
现在,函数parseJSON
的类型是Value->Parser a
。这应该被恰当地称为解析器,但为了避免混淆,我们将其称为“parse function”。解析函数接受JSON表示(一个值
,或一个对象
或其他一些JSON内容)并返回解析结果。withXXX
函数族用于在JSON内容之间调整解析函数。如果您有一个需要对象的解析函数,例如:
\v -> Xyz <$> v .: "x" <*> v .: "y" :: Object -> Parser Xyz
您希望extra
成为一个对象
,并希望使用适当的和xxx
帮助程序将其从整个JSON对象v::Object
中提取出来,以将解析函数从一种输入JSON类型调整到另一种输入JSON类型。那么,让我们编写一个一元函数(实际上是一个解析函数)来实现这一点:
getExtra :: Object -> Parser Object
getExtra v = do
首先,我们从v
中提取可选的“额外”组件。我们在这里使用条件形式,因此mextra::Maybe Value
mextra <- v .:? "extra"
case mextra of
Just vv -> vv & withArray "Abc.extra" (\arr -> do
现在,arr
是一个数组=向量值
。我们希望它是一个对象的数组。让我们将值作为列表拉出:
let vallst = toList arr
然后在withObject
的帮助下,一元遍历列表,以确保它们都是预期的Object
s。请注意此处使用的pure
,因为我们希望提取对象
s,而无需任何额外处理:
objlst <- traverse (withObject "Abc.extra[..]" pure) vallst
对于Nothing
案例(“extra”未找到),我们只返回一个空hashmap:
Nothing -> return HashMap.empty
完整函数如下所示:
getExtra :: Object -> Parser Object
getExtra v = do
mextra <- v .:? "extra"
case mextra of
Just vv -> vv & withArray "Abc.extra" (\arr -> do
let vallst = toList arr
objlst <- traverse (withObject "Abc.extra[..]" pure) vallst
return $ HashMap.unions objlst)
Nothing -> return HashMap.empty
它是这样工作的:
λ> main
Right (Abc {name = "xyz1", a = Just "Hello", b = Just "World"})
完整代码:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson (eitherDecode, FromJSON, Object, parseJSON, withArray, withObject, (.:), (.:?))
import Data.Aeson.Types (Parser)
import GHC.Generics (Generic)
import qualified Data.ByteString.Lazy as BL (ByteString)
import qualified Data.HashMap.Strict as HashMap (empty, unions)
import Data.Function ((&))
import Data.Foldable (toList)
data Abc = Abc
{ name :: String
, a :: Maybe String
, b :: Maybe String
} deriving (Generic, Show)
instance FromJSON Abc where
parseJSON =
withObject "Abc" $ \v -> do
extra <- getExtra v
Abc <$> v .: "name" <*> extra .:? "this_string_A" <*> extra .:? "this_string_B"
getExtra :: Object -> Parser Object
getExtra v = do
mextra <- v .:? "extra"
case mextra of
Just vv -> vv & withArray "Abc.extra" (\arr -> do
let vallst = toList arr
objlst <- traverse (withObject "Abc.extra[..]" pure) vallst
return $ HashMap.unions objlst)
Nothing -> return HashMap.empty
example :: BL.ByteString
example = "{\"name\": \"xyz1\", \"extra\": [{\"this_string_A\": \"Hello\"}, {\"this_string_B\": \"World\"}]}"
main = print (eitherDecode example :: Either String Abc)
{-#派生通用语言}
{-#语言重载字符串}
导入Data.Aeson(eitherDecode、FromJSON、Object、parseJSON、withArray、withObject、(.:)、(.:?)
导入Data.Aeson.Types(解析器)
进口GHC.仿制药(仿制药)
将限定数据.ByteString.Lazy导入为BL(ByteString)
将限定的Data.HashMap.Strict导入为HashMap(空,联合)
导入数据。函数(&))
导入数据。可折叠(toList)
数据Abc=Abc
{name::String
,a::可能是字符串
可能是字符串
}派生(通用、显示)
来自JSON Abc的实例,其中
parseJSON=
withObject“Abc”$\v->do
额外解析器对象
getExtra v=do
mextra vv&带有数组“Abc.extra”(\arr->do)
设vallst=toList arr
objlst返回HashMap.empty
示例::BL.ByteString
示例=“{\'name\':\'xyz1\',\'extra\':[{\'this\u string\u A\':\'Hello\'},{\'this\u string\u B\':\'World\'}”
main=print(解码示例::任意字符串Abc)
是否可以选择将extra
建模为求和类型,而不是两个Maybe
值?两个Maybe
值似乎很奇怪,因为这种设计允许四种状态,包括两者都是无
和都是仅
。您能给出两个其他输入示例吗?不清楚(至少对我来说)json中可能存在什么样的变化。例如,extra
是否始终存在?是否extra
始终包含两个元素?是否extra
的元素始终只有一个键/值对?键是否始终是此字符串A
或此字符串B
或其他键遇到?我不担心变化。如果我能让它与提供的输入一起工作,那就足够了。
objlst <- traverse (withObject "Abc.extra[..]" pure) vallst
return $ HashMap.unions objlst)
Nothing -> return HashMap.empty
getExtra :: Object -> Parser Object
getExtra v = do
mextra <- v .:? "extra"
case mextra of
Just vv -> vv & withArray "Abc.extra" (\arr -> do
let vallst = toList arr
objlst <- traverse (withObject "Abc.extra[..]" pure) vallst
return $ HashMap.unions objlst)
Nothing -> return HashMap.empty
instance FromJSON Abc where
parseJSON =
withObject "Abc" $ \v -> do
extra <- getExtra v
Abc <$> v .: "name" <*> extra .:? "this_string_A" <*> extra .:? "this_string_B"
example :: BL.ByteString
example = "{\"name\": \"xyz1\", \"extra\": [{\"this_string_A\": \"Hello\"}, {\"this_string_B\": \"World\"}]}"
main = print (eitherDecode example :: Either String Abc)
λ> main
Right (Abc {name = "xyz1", a = Just "Hello", b = Just "World"})
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson (eitherDecode, FromJSON, Object, parseJSON, withArray, withObject, (.:), (.:?))
import Data.Aeson.Types (Parser)
import GHC.Generics (Generic)
import qualified Data.ByteString.Lazy as BL (ByteString)
import qualified Data.HashMap.Strict as HashMap (empty, unions)
import Data.Function ((&))
import Data.Foldable (toList)
data Abc = Abc
{ name :: String
, a :: Maybe String
, b :: Maybe String
} deriving (Generic, Show)
instance FromJSON Abc where
parseJSON =
withObject "Abc" $ \v -> do
extra <- getExtra v
Abc <$> v .: "name" <*> extra .:? "this_string_A" <*> extra .:? "this_string_B"
getExtra :: Object -> Parser Object
getExtra v = do
mextra <- v .:? "extra"
case mextra of
Just vv -> vv & withArray "Abc.extra" (\arr -> do
let vallst = toList arr
objlst <- traverse (withObject "Abc.extra[..]" pure) vallst
return $ HashMap.unions objlst)
Nothing -> return HashMap.empty
example :: BL.ByteString
example = "{\"name\": \"xyz1\", \"extra\": [{\"this_string_A\": \"Hello\"}, {\"this_string_B\": \"World\"}]}"
main = print (eitherDecode example :: Either String Abc)