我有一个很大的结构,我需要作为FromJSON的实例,以便可以将json数据解析为其中。
我想自动派生,但是单个字段需要“特殊照顾”,因为它是json中的对象,并且我希望它是结构中值的数组。如何在不编写重复所有字段的大型FromJson实现的情况下做到这一点?
示例json:
{"myobject": {"one": 1, "two": 2}, ...many_more_fields...}
示例结构:
data MyStruct = MyStruct {
myobject :: [Int],
...many_more_fields,...
} deriving (Generic)
我如何优雅地做到这一点?
答案 0 :(得分:3)
您应该为您的特殊字段创建一个newtype
:
newtype MySpecialType = MySpecialType [Int]
instance FromJSON MySpecialType where ....
data MyStruct = MyStruct {
myobject:: MySpecialType,
...
}
现在MyStruct
的实例变得完全规则,可以按常规方式移交给Template Haskell。
答案 1 :(得分:0)
为避免整个代码库中都携带Paul Johnson's very good answer中的新类型,您还可以如下概括您的类型,将myobject
的类型作为参数:
data MyStruct_ intList = MyStruct {
myobject :: intlist,
...
} deriving (Functor, Generic)
type MyStruct = MyStruct [Int]
instance FromJSON MyStruct where
parseJSON = (fmap . fmap) (\(MySpecialType i) -> i)
. genericParseJSON defaultOptions
上面的 genericParseJSON
用MyStruct MySpecialType
实例化,然后通过fmap
解包了该字段(注意MyStruct_
是Functor
)
我也刚刚写了一个blogpost about "type surgery",适用于此类问题,因此您可以保持原始类型不变。
The generic-data-surgery library可以派生具有与上述Generic
相同的MyStruct_ MySpecialType
结构的泛型类型,以供aeson的genericParseJSON
使用。然后,手术modifyRField
将函数\(MySpecialType i) -> i
应用于myobject
字段,最后产生MyStruct
。
import Generic.Data.Surgery (fromOR, toOR', modifyRField)
-- The original type
data MyStruct = MyStruct {
myobject :: [Int],
...
} deriving (Generic)
instance FromJSON MyStruct where
parseJSON = fmap (fromOR . modifyRField @"myobject" (\(MySpecialType i) -> i) . toOR')
. genericParseJSON defaultOptions