Aeson: derive some (but not all) fields of a struct

◇◆丶佛笑我妖孽 提交于 2019-12-10 11:03:58

问题


I have a large struct which I need to be an instance of FromJSON so that I can parse my json data into it.

I would like to derive automatically, but a single field needs "special care" in that it is an object in json and I want it to be an array of the values in my struct. How can I do this without writing a huge FromJson implementation repeating all the fields?

Example json:

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

Example struct:

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

How do I do this elegantly?


回答1:


You should create a newtype for your special field:

newtype MySpecialType = MySpecialType [Int]

instance FromJSON MySpecialType where ....

data MyStruct = MyStruct {
      myobject:: MySpecialType,
      ...
   }

Now the instance for MyStruct becomes entirely regular and can be handed off to Template Haskell in the normal way.




回答2:


To avoid carrying the newtype from Paul Johnson's very good answer all across the codebase, you can also generalize your type as follows, making the type of myobject a parameter:

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 above gets instantiated with MyStruct MySpecialType, and then the field gets unwrapped via fmap (noting MyStruct_ is a Functor)


I also just wrote a blogpost about "type surgery", applied to this kind of problem so that you can keep the original type unmodified.

The generic-data-surgery library can derive a generic type with the same Generic structure as MyStruct_ MySpecialType above, to be used by aeson's genericParseJSON. The surgery modifyRField then applies the function \(MySpecialType i) -> i to the myobject field, finally yielding 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


来源:https://stackoverflow.com/questions/53448958/aeson-derive-some-but-not-all-fields-of-a-struct

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!