【问题标题】:Haskell Aeson with Sum TypesHaskell Aeson 与 Sum 类型
【发布时间】:2018-04-21 01:58:15
【问题描述】:

我有一个 sum 数据类型,如下所示:

data Declaration =
    IndDecl { what :: String, name :: String, argnames :: Maybe [String], constructors :: [Constructor] }
    | TypeDecl { what :: String, name :: String, argnames :: Maybe [String], value :: Maybe Arg }
    | FixDecl { what :: String, fixlist :: Maybe [Fixitem] }
    | TermDecl { what :: String, name :: String, typ :: Maybe Typ, value :: Maybe Arg }
    deriving (Show, Eq)

我正在使用 Data.Aeson.TH 和以下选项派生 JSON 序列化器/反序列化器:

$(deriveJSON defaultOptions { sumEncoding = UntaggedValue } ''Declaration)

这会从 JSON 构造中删除标签。同时,我觉得我的数据已经有了一个标签,就是what这个字段。它可以是以下之一:

IndDecl : "decl:ind" 
FixDecl : "decl:fix"

等等。因此,我为我的 JSON 反序列化对象使用 Untagged 配置这一事实有点令人担忧。如何让 Aeson 根据我的数据已有的标签派生 JSON 存根?

或者删除what字段并让Aeson添加一个描述构造函数的“标签”字段可能是一个更好的主意?

编辑:我通过添加标签字段尝试了后者。现在看起来像:

-- Declarations
data Declaration =
    IndDecl { what :: String, tag :: String, name :: String, argnames :: Maybe [String], constructors :: [Constructor] }
    | TypeDecl { what :: String, tag :: String, name :: String, argnames :: Maybe [String], value :: Maybe Arg }
    | FixDecl { what :: String, tag :: String, fixlist :: Maybe [Fixitem] }
    | TermDecl { what :: String, tag :: String, name :: String, typ :: Maybe Typ, value :: Maybe Arg }
    deriving (Show, Eq)

但是,我收到以下错误:

$.declarations[0] 中的错误:键“标签”不存在

我不明白,因为我创建了一个“标签”字段。

【问题讨论】:

  • 您误解了 Aeson 的工作原理。尝试序列化您的数据,看看会发生什么。
  • Data.Aeson.TH documentation 建议您希望 sumEncoding = defaultTaggedObject { tagFieldName = "what" } 使用您预先存在的字段作为标签。
  • 数据声明中不需要what字段。你已经有了它的构造函数。 AESON 中的不同标签选项仅与 JSON 本身相关,与 Haskell 结构无关。因此,只需删除 what 字段即可。
  • 我同意从我的结构中删除“what”字段,但是即使我添加了标签,最后一个错误key "tag" not present 又如何呢? (见编辑)

标签: haskell types aeson


【解决方案1】:

如果您可以控制 JSON 的结构,这意味着您不尝试匹配现有的 JSON,我将使用 ToJSONFromJSON 的通用实例。但是,这意味着您不能有一个表示tag 的选择器。泛型和模板 Haskell 实例为 sum 类型的构造函数保留关键字 tag,因为不同的构造函数可能具有类似命名的选择器(您的示例就是这种情况,或者它们可能具有包含类似类型的未命名选择器,例如 @987654325 @。

{-# LANGUAGE DeriveGeneric #-}

import Data.Aeson
import GHC.Generics

-- I added these so it would compile
type Arg = String
type Fixitem = String
type Typ = String
type Constructor = String

data Declaration 
  = IndDecl 
    { what :: String
    , name :: String
    , argnames :: Maybe [String]
    , constructors :: [Constructor] 
    }
  | TypeDecl 
    { what :: String
    , name :: String
    , argnames :: Maybe [String]
    , value :: Maybe Arg 
    }
  | FixDecl 
    { what :: String
    , fixlist :: Maybe [Fixitem]
    }
  | TermDecl 
    { what :: String
    , name :: String
    , typ :: Maybe Typ
    , value :: Maybe Arg 
    }
  deriving (Show, Eq, Generic)

instance ToJSON Declaration
instance FromJSON Declaration

现在我们可以将 Haskell 值转换为 JSON:

λ> encode $ IndDecl "a" "b" (Just ["c", "d"]) ["e"]
"{\"tag\":\"IndDecl\",\"what\":\"a\",\"argnames\":[\"c\",\"d\"],\"constructors\":[\"e\"],\"name\":\"b\"}"

我们还可以将 JSON 字符串转换回 Declaration 类型的值。

λ> decode $ "{\"tag\":\"FixDecl\",\"what\":\"a\"}" :: (Maybe Declaration)
Just (FixDecl {what = "a", fixlist = Nothing})

λ> decode $ "{\"tag\":\"TermDecl\",\"what\":\"a\",\"name\":\"c\",\"typ\":\"e\",\"value\":\"b\"}" :: (Maybe Declaration)
Just (TermDecl {what = "a", name = "c", typ = Just "e", value = Just "b"})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多