【发布时间】:2017-10-22 17:39:15
【问题描述】:
我正在尝试解析 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 关联的数组中包含的记录也不需要特殊解析。但是,在解析objects 数组中的记录的某些部分,单独考虑时,已经记录了解决方案,但一起提出了我还没有弄清楚如何解决的问题。
现在,objects 和 parameters 数组中可能的记录变体数量有限,并且通常包含相同的键;例如,它们都有一个“name”键或一个“id”键,等等。但它们中的许多也有一个“类型”键,这是一个保留关键字,因此不能通用解析。这是第一个问题。
第二个问题是objects 内的记录的一种可能变体可以有一个键——比如说“依赖”——它的值可能采用不同的类型。可以是单条记录
{
"objects": [
{
"depends": {
"reference": "r1"
},
...
],
...
}
或记录列表
{
"objects": [
"depends": [
{"reference": "r1"},
{"reference": "r2"},
etc.
],
],
...
}
碰巧这是我想在转换为 Haskell 对象后以自定义方式操作的一个字段(最终我想将此类“依赖”引用的集合表示为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)
这一直有效,直到它尝试解析“依赖”字段并报告该字段
"Error in $.objects[2].depends: key \"tag\" not present"
没有“标签”键,所以我不确定这是什么意思。我怀疑这与Ref 和Reference 的FromJSON 的通用实例有关。
我的问题:
- 此错误表示什么?到目前为止,在我学习 Haskell 的过程中,这些错误一直很有帮助。这个不是。我需要为
parseJSON函数中的“depends”键做一些特别的事情吗? - 所有这些样板实际上都是因为两个键——“类型”和“依赖”。有没有更优雅的方式来处理这些键?
- 相关地,这是我的第一个 真正 Haskell 项目的一部分,所以我有一个更一般的设计问题。有经验的 Haskellers 和 Aeson 用户,你会如何为这种类型的 JSON 布置你的类型和实例?我尝试将
objects/parameters记录的每个可能变体列为其自己的单独类型,并且只为具有“依赖”或“类型”键的那些编写自定义FromJSON实例,但这产生了更多样板代码,无论如何都不能解决我遇到的任何其他问题。关于“最佳实践”、惯用用法等的一般指示将非常有用和赞赏。
【问题讨论】:
-
试着把它归结为一个问题。您的第三个问题可以在您的代码运行后立即在Code Review 上回答。