Parsing 是否可以将一个不断变化的JSON键与一个在较大记录类型中包含aeson的sum类型数据构造函数相匹配?
因此,我有一个数据类型Parsing 是否可以将一个不断变化的JSON键与一个在较大记录类型中包含aeson的sum类型数据构造函数相匹配?,parsing,haskell,aeson,Parsing,Haskell,Aeson,因此,我有一个数据类型ItemType,它使用其数据构造函数名称进行解码(请参阅FromJSON实例) 导入数据.Aeson 导入Data.Aeson.Types 导入Data.Char(toLower) 进口GHC.仿制药 数据项类型= MkLogin登录 |明信片 |MkIdentity身份 |MkSecureNote注释 派生(通用、显示) 小写::字符串->字符串 小写“=” 小写字母(s:ss)=toLower s:ss stripPrefix::String->String stri
ItemType
,它使用其数据构造函数名称进行解码(请参阅FromJSON实例)
导入数据.Aeson
导入Data.Aeson.Types
导入Data.Char(toLower)
进口GHC.仿制药
数据项类型=
MkLogin登录
|明信片
|MkIdentity身份
|MkSecureNote注释
派生(通用、显示)
小写::字符串->字符串
小写“=”
小写字母(s:ss)=toLower s:ss
stripPrefix::String->String
stripPrefix('M':'k':ss)=ss
stripPrefix str=str
--|使用ItemType数据构造函数名称解码值
实例FromJSON ItemType,其中
parseJSON=genericParseJSON默认选项
{constructorTagModifier=小写.stripPrefix
,sumcodencing=ObjectWithSingleField}
我想做的是将这个类型作为字段添加到一个更大的记录类型中,该记录类型名为Item
数据项=
项{u对象::字符串
,_id::String
,_organizationId::可能是Int
,_folderId::可能是Int
,_类型::Int
,_name::String
,_注释::字符串
你最喜欢的是什么
,???::ItemType--不知道如何在没有其他字段名的情况下添加此项
,_collectionId::[Int]
,_修订日期::可能是字符串
}派生(通用、显示)
实例FromJSON项,其中
parseJSON=
genericParseJSON默认选项{fieldLabelModifier=Strip下划线}
但是,我不想为该类型创建新的字段名。相反,我想使用aeson在ItemType
上匹配的数据构造函数作为字段名,因为我试图根据ItemType
是什么来建模JSON对象中ItemType
字段的键。因此,在本例中,密钥是“登录”、“卡”、“身份”、“安全注意”。也许我应该使用TaggedObject
进行sumcodeding
,但我不完全确定它是如何工作的
项
对象的JSON列表示例:。在这里,您可以通过键“login”、“card”、“identity”查看ItemType
字段,具体取决于它们是什么类型。一种方法是在项
数据声明中没有ItemType
字段。然后使用元组或自定义对类型来保存这两个片段;因此:
data ItemWithType = ItemWithType ItemType Item
instance FromJSON ItemWithType where
parseJSON v = liftA2 ItemWithType (parseJSON v) (parseJSON v)
您也可以跳过定义ItemWithType
,只需使用
\o -> liftA2 (,) (parseJSON o) (parseJSON o)
直接解析具有一致名称的字段元组和变量键下的对象。您可以使用一种相当难看的方法来预处理传入的JSON
值
,以便实际的JSON输入如下:
{
"id": "foo",
"bool": false
}
将其解析为:
{
"id": "foo",
"itemtype": {"bool" : false}
}
通用解析器可以使用ObjectWithSingleField
sum编码方法直接处理
作为一个简化示例,给出:
data ItemType =
MkInt Int
| MkBool Bool
deriving (Generic, Show)
instance FromJSON ItemType where
parseJSON = genericParseJSON defaultOptions
{ constructorTagModifier = map toLower . \('M':'k':ss) -> ss
, sumEncoding = ObjectWithSingleField }
以及:
您可以为Item
编写一个FromJSON
实例,该实例将“int”
或“bool”
字段嵌套在“itemtype”
字段中。(原始字段的副本保留在原位,但被通用解析器忽略。)
实例FromJSON项,其中
parseJSON v=do
v'ss}v'
其中nest o=对象(HM.insert“itemtype”item纯o)
其中item=subObj“int”subObj“bool”fail“no item type field”
子对象k=(\v->object[(k,v)])o.:k
完整代码:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
import Control.Applicative
import Data.Aeson
import Data.Aeson.Types
import Data.Char (toLower)
import GHC.Generics
import qualified Data.HashMap.Strict as HM
data ItemType =
MkInt Int
| MkBool Bool
deriving (Generic, Show)
instance FromJSON ItemType where
parseJSON = genericParseJSON defaultOptions
{ constructorTagModifier = map toLower . \('M':'k':ss) -> ss
, sumEncoding = ObjectWithSingleField }
data Item =
Item { _id :: String
, _itemtype :: ItemType
}
deriving (Generic, Show)
instance FromJSON Item where
parseJSON v = do
v' <- withObject "Item" nest v
genericParseJSON defaultOptions { fieldLabelModifier = \('_':ss) -> ss } v'
where nest o = Object <$> (HM.insert "itemtype" <$> item <*> pure o)
where item = subObj "int" <|> subObj "bool" <|> fail "no item type field"
subObj k = (\v -> object [(k,v)]) <$> o .: k
test1, test2, test3 :: Either String Item
test1 = eitherDecode "{\"id\":\"foo\",\"bool\":false}"
test2 = eitherDecode "{\"id\":\"foo\",\"int\":10}"
test3 = eitherDecode "{\"id\":\"foo\"}"
main = do
print test1
print test2
print test3
{-#派生通用语言}
{-#语言重载字符串}
{-#语言元组{-}
导入控制
导入数据.Aeson
导入Data.Aeson.Types
导入Data.Char(toLower)
进口GHC.仿制药
导入符合条件的Data.HashMap.Strict作为HM
数据项类型=
MkInt
|MkBool Bool
派生(通用、显示)
实例FromJSON ItemType,其中
parseJSON=genericParseJSON默认选项
{constructorTagModifier=map-toLower.\('M':'k':ss)->ss
,sumcodencing=ObjectWithSingleField}
数据项=
项{u id::String
,_itemtype::itemtype
}
派生(通用、显示)
实例FromJSON项,其中
parseJSON v=do
v'ss}v'
其中nest o=对象(HM.insert“itemtype”item纯o)
其中item=subObj“int”subObj“bool”fail“no item type field”
子对象k=(\v->object[(k,v)])o.:k
test1、test2、test3::任意一个字符串项
test1=eitherDecode“{\“id\:\“foo\”,“bool\”:false}”
test2=eitherDecode“{\“id\:\“foo\”,“int\”:10}”
test3=eitherDecode“{\“id\:\“foo\”}”
main=do
打印测试1
打印测试2
打印测试3
不过,一般来说,除非你经常这样做,否则为了清晰易懂,最好还是放弃泛型,编写必要的样板。即使是你最初的例子,也没有那么繁重。是的,您必须保持类型和实例的同步,但是几个简单的测试应该可以发现任何问题。例如,类似于:
instance FromJSON Item where
parseJSON = withObject "Item" $ \o ->
Item <$> o .: "object"
<*> o .: "id"
<*> o .:? "organizationId"
<*> o .:? "folderId"
<*> o .: "type"
<*> o .: "name"
<*> o .: "notes"
<*> o .: "favorite"
<*> parseItemType o
<*> o .: "collectionIds"
<*> o .:? "revisionDate"
where parseItemType o =
MkLogin <$> o .: "login"
<|> MkCard <$> o .: "card"
<|> MkIdentity <$> o .: "identity"
<|> MkSecureNote <$> o .: "securenote"
实例FromJSON项,其中
parseJSON=withObject“项”$\o->
项目o.:“对象”
o.:“id”
o.:?“组织ID”
o.:?“福尔德里德”
o.:“类型”
o.:“姓名”
o.:“注意事项”
o.:“最爱”
parseItemType o
o.:“集合ID”
o.:?“修订日期”
其中parseItemType为o=
MkLogin o.:“登录”
MkCard o.:“卡”
MKO标识:“标识”
MkSecureNote o.:“securenote”
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
import Control.Applicative
import Data.Aeson
import Data.Aeson.Types
import Data.Char (toLower)
import GHC.Generics
import qualified Data.HashMap.Strict as HM
data ItemType =
MkInt Int
| MkBool Bool
deriving (Generic, Show)
instance FromJSON ItemType where
parseJSON = genericParseJSON defaultOptions
{ constructorTagModifier = map toLower . \('M':'k':ss) -> ss
, sumEncoding = ObjectWithSingleField }
data Item =
Item { _id :: String
, _itemtype :: ItemType
}
deriving (Generic, Show)
instance FromJSON Item where
parseJSON v = do
v' <- withObject "Item" nest v
genericParseJSON defaultOptions { fieldLabelModifier = \('_':ss) -> ss } v'
where nest o = Object <$> (HM.insert "itemtype" <$> item <*> pure o)
where item = subObj "int" <|> subObj "bool" <|> fail "no item type field"
subObj k = (\v -> object [(k,v)]) <$> o .: k
test1, test2, test3 :: Either String Item
test1 = eitherDecode "{\"id\":\"foo\",\"bool\":false}"
test2 = eitherDecode "{\"id\":\"foo\",\"int\":10}"
test3 = eitherDecode "{\"id\":\"foo\"}"
main = do
print test1
print test2
print test3
instance FromJSON Item where
parseJSON = withObject "Item" $ \o ->
Item <$> o .: "object"
<*> o .: "id"
<*> o .:? "organizationId"
<*> o .:? "folderId"
<*> o .: "type"
<*> o .: "name"
<*> o .: "notes"
<*> o .: "favorite"
<*> parseItemType o
<*> o .: "collectionIds"
<*> o .:? "revisionDate"
where parseItemType o =
MkLogin <$> o .: "login"
<|> MkCard <$> o .: "card"
<|> MkIdentity <$> o .: "identity"
<|> MkSecureNote <$> o .: "securenote"