使用 Aeson 解析有问题的 JSON
Parsing problematic JSON with Aeson
我正在尝试解析 JSON 个对象,它们的形式通常是
{
"objects": [a bunch of records that can assume a few different forms],
"parameters": [same deal],
"values": {
"k1": "v1",
"k2": "v2",
...
}
}
使用 Haskell 的 Aeson 库。这个任务的一部分很简单,因为 parameters
和 values
字段不需要任何自定义解析(因此似乎只需要 FromJSON
的一般派生实例),并且 [=与 objects
关联的数组中包含的 57=] 大多数 记录也不需要特殊解析。但是,解析 objects
数组中的记录的某些部分,当单独考虑时,有记录的解决方案,但一起提出了我还没有想出如何解决的问题。
现在,objects
和 parameters
数组中 record 的可能变体在数量上是有限的,并且通常包含相同的键;例如,它们都有一个 "name" 键或 "id" 键,等等。但它们中的许多都有一个 "type" 键,这是一个保留关键字,因此无法进行一般解析。这是第一个问题。
第二个问题是 objects
中记录的一种可能变体可能有一个键——比方说 "depends"——其值可能采用不同的类型。它可以是单个记录
{
"objects": [
{
"depends": {
"reference": "r1"
},
...
],
...
}
或记录列表
{
"objects": [
"depends": [
{"reference": "r1"},
{"reference": "r2"},
etc.
],
],
...
}
碰巧这是我想在转换为 Haskell 对象后以自定义方式操作的一个字段(最终我想表示此类 "depends" 引用的集合作为 Data.Graph
图)。
我最初的尝试是创建一个巨大的记录类型,它包含 objects
和 parameters
数组元素中所有可能的键。像这样:
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
import Data.Aeson
import GHC.Generics
data Ref = Ref
{ ref :: String
} deriving (Show, Generic, FromJSON, ToJSON)
data Reference
= Reference Ref
| References [Ref]
deriving (Show, Generic, FromJSON, ToJSON)
type MString = Maybe String -- I'm writing this a lot using this approach
data PObject = PObject
-- Each of the object/parameter records have these keys
{ _name :: String
, _id :: String
-- Other keys that might appear in a given object/parameter record
, _type :: MString
, _role :: MString
, _depends :: Maybe Reference
-- A bunch more
} deriving Show
instance FromJSON PObject where
parseJSON = withObject "PObject" $ \o -> do
_name <- o .: "name"
_id <- o .: "id"
_type <- o .:? "type"
_role <- o .:? "role"
_depends <- o .:? "depends"
-- etc.
return PObject{..}
最后,整个 JSON 对象将被表示为
data MyJSONObject = MyJSONObject
{ objects :: Maybe [PObject]
, parameters :: Maybe [PObject]
, values :: Maybe Object
} deriving (Show, Generic, FromJSON)
这一直有效,直到它尝试解析 "depends" 字段,并报告
"Error in $.objects[2].depends: key \"tag\" not present"
没有 "tag" 键,所以我不确定这是什么意思。我怀疑它与 Ref
和 Reference
的 FromJSON
的通用实例有关。
我的问题:
- 这个错误说明了什么?到目前为止,在我学习 Haskell 的过程中,错误总是很有帮助。这个不是。我是否需要为我的
parseJSON
函数中的 "depends" 键做一些特殊的事情?
- 所有这些样板实际上都是因为两个键——"type" 和 "depends"。有没有更优雅的方式来处理这些键?
- 相关地,这是我第一个 real Haskell 项目的一部分,所以我有一个更一般的设计问题。有经验的 Haskeller 和 Aeson 用户,您如何为这种类型的 JSON 布置您的类型和实例?我尝试列出
objects
/parameters
记录的每个可能变体作为其自己的单独类型,并且只为那些具有 "depends" 或 [=73] 的实例编写自定义 FromJSON
实例=] 键,但这会产生更多的样板代码,并且无论如何都不能解决我遇到的任何其他问题。关于 "best practices"、惯用用法等的一般指示将非常有用并受到赞赏。
There are no "tag" keys, so I'm not sure what this means. I suspect it has to do with the generic instances of FromJSON
for Ref
and Reference
.
说得对。通过 default, aeson will use the defaultTaggedObject
来编码总和类型。 References
是求和类型。所以aeson引入了一个tag来区分构造函数。你可以用一个简短的例子来尝试:
ghci> data Example = A () | B deriving (Generic,ToJSON)
ghci> encode B
"{\"tag\":\"B\",\"contents\":[]}"
当您使用 _depends <- o .:? "depends"
时,Reference
解析器找不到它的标签。你必须自己在那里写一些解析代码。
All of this boilerplate is really because of two keys -- "type" and
"depends". Is there a more elegant way to deal with these keys?
您可以保留字段名称中的下划线,并使用 fieldLabelModifier
in the Options
数据类型将其剥离以进行解析。
我正在尝试解析 JSON 个对象,它们的形式通常是
{
"objects": [a bunch of records that can assume a few different forms],
"parameters": [same deal],
"values": {
"k1": "v1",
"k2": "v2",
...
}
}
使用 Haskell 的 Aeson 库。这个任务的一部分很简单,因为 parameters
和 values
字段不需要任何自定义解析(因此似乎只需要 FromJSON
的一般派生实例),并且 [=与 objects
关联的数组中包含的 57=] 大多数 记录也不需要特殊解析。但是,解析 objects
数组中的记录的某些部分,当单独考虑时,有记录的解决方案,但一起提出了我还没有想出如何解决的问题。
现在,objects
和 parameters
数组中 record 的可能变体在数量上是有限的,并且通常包含相同的键;例如,它们都有一个 "name" 键或 "id" 键,等等。但它们中的许多都有一个 "type" 键,这是一个保留关键字,因此无法进行一般解析。这是第一个问题。
第二个问题是 objects
中记录的一种可能变体可能有一个键——比方说 "depends"——其值可能采用不同的类型。它可以是单个记录
{
"objects": [
{
"depends": {
"reference": "r1"
},
...
],
...
}
或记录列表
{
"objects": [
"depends": [
{"reference": "r1"},
{"reference": "r2"},
etc.
],
],
...
}
碰巧这是我想在转换为 Haskell 对象后以自定义方式操作的一个字段(最终我想表示此类 "depends" 引用的集合作为 Data.Graph
图)。
我最初的尝试是创建一个巨大的记录类型,它包含 objects
和 parameters
数组元素中所有可能的键。像这样:
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
import Data.Aeson
import GHC.Generics
data Ref = Ref
{ ref :: String
} deriving (Show, Generic, FromJSON, ToJSON)
data Reference
= Reference Ref
| References [Ref]
deriving (Show, Generic, FromJSON, ToJSON)
type MString = Maybe String -- I'm writing this a lot using this approach
data PObject = PObject
-- Each of the object/parameter records have these keys
{ _name :: String
, _id :: String
-- Other keys that might appear in a given object/parameter record
, _type :: MString
, _role :: MString
, _depends :: Maybe Reference
-- A bunch more
} deriving Show
instance FromJSON PObject where
parseJSON = withObject "PObject" $ \o -> do
_name <- o .: "name"
_id <- o .: "id"
_type <- o .:? "type"
_role <- o .:? "role"
_depends <- o .:? "depends"
-- etc.
return PObject{..}
最后,整个 JSON 对象将被表示为
data MyJSONObject = MyJSONObject
{ objects :: Maybe [PObject]
, parameters :: Maybe [PObject]
, values :: Maybe Object
} deriving (Show, Generic, FromJSON)
这一直有效,直到它尝试解析 "depends" 字段,并报告
"Error in $.objects[2].depends: key \"tag\" not present"
没有 "tag" 键,所以我不确定这是什么意思。我怀疑它与 Ref
和 Reference
的 FromJSON
的通用实例有关。
我的问题:
- 这个错误说明了什么?到目前为止,在我学习 Haskell 的过程中,错误总是很有帮助。这个不是。我是否需要为我的
parseJSON
函数中的 "depends" 键做一些特殊的事情? - 所有这些样板实际上都是因为两个键——"type" 和 "depends"。有没有更优雅的方式来处理这些键?
- 相关地,这是我第一个 real Haskell 项目的一部分,所以我有一个更一般的设计问题。有经验的 Haskeller 和 Aeson 用户,您如何为这种类型的 JSON 布置您的类型和实例?我尝试列出
objects
/parameters
记录的每个可能变体作为其自己的单独类型,并且只为那些具有 "depends" 或 [=73] 的实例编写自定义FromJSON
实例=] 键,但这会产生更多的样板代码,并且无论如何都不能解决我遇到的任何其他问题。关于 "best practices"、惯用用法等的一般指示将非常有用并受到赞赏。
There are no "tag" keys, so I'm not sure what this means. I suspect it has to do with the generic instances of
FromJSON
forRef
andReference
.
说得对。通过 default, aeson will use the defaultTaggedObject
来编码总和类型。 References
是求和类型。所以aeson引入了一个tag来区分构造函数。你可以用一个简短的例子来尝试:
ghci> data Example = A () | B deriving (Generic,ToJSON)
ghci> encode B
"{\"tag\":\"B\",\"contents\":[]}"
当您使用 _depends <- o .:? "depends"
时,Reference
解析器找不到它的标签。你必须自己在那里写一些解析代码。
All of this boilerplate is really because of two keys -- "type" and "depends". Is there a more elegant way to deal with these keys?
您可以保留字段名称中的下划线,并使用 fieldLabelModifier
in the Options
数据类型将其剥离以进行解析。