具有多个带有分隔标签的构造器的解码对象

问题描述 投票:1回答:1

我有表格的数据

fooooo
{"a": 123}

barrrr
{"a": 123, "b": 123}

fooooo
{"a": 123}

我想将其解析为相同的数据类型:

data Test
  = Foo
    { a :: Int
    , b :: Int
    }
  | Bar
    { a :: Int
    }
  deriving (Show, Generic)

在Aeson中有一个tag feature,但似乎需要在对象内部存在标签。

是否有某种方式可以处理它(带标签在外面)

magicDecode :: String -> Test
magicDecode "fooooo" = decodeSpecyfic Foo
magicDecode "barrrr" = decodeSpecyfic Bar

或类似的东西?

json haskell generics aeson
1个回答
0
投票

您可以使用UntaggedValue选项:

{-# LANGUAGE DeriveGeneric #-}
module Q59916344 where

import Data.Aeson
import GHC.Generics

myOptions = defaultOptions { sumEncoding = UntaggedValue }

data Test = Foo { a :: Int, b :: Int } | Bar { a :: Int } deriving (Show, Generic)

instance FromJSON Test where
  parseJSON = genericParseJSON myOptions

instance ToJSON Test where
  toJSON     = genericToJSON myOptions
  toEncoding = genericToEncoding myOptions

正如the documentation解释UntaggedValue

解码时,按定义顺序尝试构造器。如果某些编码重叠,则定义的第一个编码将成功。

演示:

*Q59916344 Q59916344> decode "{\"a\": 123}" :: Maybe Test
Just (Bar {a = 123})
*Q59916344 Q59916344> decode "{\"a\": 123, \"b\": 123}" :: Maybe Test
Just (Foo {a = 123, b = 123})

也就是说,您不应该用记录来建模求和类型],因为记录访问器是局部的:

*Q59916344 Q59916344> b (Foo 42 1337)
1337
*Q59916344 Q59916344> b (Bar 42)
*** Exception: No match in record selector b
© www.soinside.com 2019 - 2024. All rights reserved.