Aeson:派生结构的某些(但不是全部)字段

时间:2018-11-23 15:01:25

标签: json haskell aeson

我有一个很大的结构,我需要作为FromJSON的实例,以便可以将json数据解析为其中。

我想自动派生,但是单个字段需要“特殊照顾”,因为它是json中的对象,并且我希望它是结构中值的数组。如何在不编写重复所有字段的大型FromJson实现的情况下做到这一点?

示例json:

{"myobject": {"one": 1, "two": 2}, ...many_more_fields...}

示例结构:

data MyStruct = MyStruct {
  myobject :: [Int],
  ...many_more_fields,...
} deriving (Generic)

我如何优雅地做到这一点?

2 个答案:

答案 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
上面的

genericParseJSONMyStruct 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