Обработка `id` в производных экземплярах Aeson FromJSON с помощью Aeson/JSON

Если у меня есть JSON и я пытаюсь автоматически получить экземпляры FromJSON с помощью Generics, у меня возникнут проблемы с id, существующими более чем в одном месте в JSON.

Есть ли способ переопределить только часть id или мне нужно написать весь экземпляр, чтобы изменить эти конкретные записи? В JSON на самом деле больше полей, но в этом примере я пропустил большую их часть. Так что на самом деле довольно утомительно записывать весь экземпляр FromJSON.

JSON:

{
  "response": [
    {
      "id": 1,
      "brandId": 1,
      "productTypeId": 1,
      "identity": {
        "sku": "x",
        "barcode": "Ax"
      },
      "stock": {
        "stockTracked": false,
        "weight": {
          "magnitude": 0
        },
        "dimensions": {
          "length": 0,
          "height": 0,
          "width": 0,
          "volume": 0
        }
      },
      "financialDetails": {
        "taxable": false,
        "taxCode": {
          "id": 1,
          "code": "x"
        }
      },
... etc
  ]
}

КОД Пока:

data Response = Response
    { response :: [Body]
    } deriving (Show,Generic)

data Body = Body
    { id                    :: Int
    , brandId               :: Int
    , productTypeId         :: Int
    , identity              :: Identity
    , productGroupId        :: Int
    , stock                 :: Stock
    , financialDetails      :: FinancialDetails
    } deriving (Show,Generic)                  

data Identity = Identity
    { sku       :: String
    , ean       :: String
    , barcode   :: String
    } deriving (Show,Generic)                  

data Stock = Stock
    { stockTracked  :: Bool
    , weight        :: Weight
    , dimensions    :: Dimensions
    } deriving (Show,Generic)                  

data Weight = Weight
    { magnitude  :: Int
    } deriving (Show,Generic)                  

data Dimensions = Dimensions
    { length :: Int
    , height :: Int
    , width  :: Int
    , volume :: Int
    } deriving (Show,Generic)                  

data FinancialDetails = FinancialDetails
     { taxable :: Bool
     , taxCode :: TaxCode
     } deriving (Show,Generic)                  

data TaxCode = TaxCode
     { id      :: Int
     , code    :: String 
     } deriving (Show,Generic)                  


instance FromJSON Response
instance FromJSON Body
instance FromJSON Identity
instance FromJSON Stock
instance FromJSON Weight
instance FromJSON Dimensions
instance FromJSON FinancialDetails

Это дает ошибку:

[1 of 1] Compiling Main             ( reponse.hs, interpreted )

response.hs:73:8:
    Multiple declarations of `id'
    Declared at: response.hs:19:7
                 response.hs:73:8
Failed, modules loaded: none.

В идеале я хотел бы изменить первое id на body_id, а второе на taxCode_id без необходимости записи всего экземпляра.


person matt    schedule 29.02.2016    source источник
comment
Предстоящий GHC 8.0 будет иметь расширение DuplicateRecordFields ghc.haskell.org/trac /ghc/wiki/Records/OverloadedRecordFields/, который решит эту проблему.   -  person danidiaz    schedule 01.03.2016


Ответы (3)


При получении экземпляров FromJSON вы можете передать параметр функции genericParseJSON. обычно это

data Foo = {- ... -} deriving (Show, Generic)

instance FromJSON Foo where
    parseJSON = genericParseJSON defaultOptions
    -- defaultOptions :: Options

в то время как вы можете заменить defaultOptions на Option, который вы создали вручную. Тип Option имеет поле fieldLabelModifier, которое может предварительно обрабатывать имя поля вашего типа данных. Вы можете определить свой тип данных как

data Body = Body
  { body_id :: Int
  ...

И напишите вспомогательную функцию, которая отображает "body_id" в "id" и все остальное без изменений:

body_noprefix "body_id" = "id"
body_noprefix s = s

Затем определите экземпляр как

instance FromJSON Body where
  parseJSON = genericParseJSON (defaultOptions { fieldLabelModifier = body_noprefix })
person zakyggaps    schedule 29.02.2016
comment
Ницца! Я не знал, что вы можете предоставить параметры для общих экземпляров. - person Christoph Hegemann; 29.02.2016
comment
Это то, что мне нужно. Спасибо за помощь!. - person matt; 01.03.2016

Проблема не в том, что GHC не может предоставить Generics, а в том, что в Haskell метки записей также являются функциями доступа, и поэтому вы получите конфликт имен, если попытаетесь использовать одну и ту же метку для двух разных записей.

Если вы используете функции из Data.Aeson.TH, вы можете использовать опцию fieldLabelModifier, например. удалите префиксы из ваших ярлыков.

data Identity = Identity
{ identitysku       :: String
, identityean       :: String
, identitybarcode   :: String
} deriving (Show)

$(deriveJSON defaultOptions{fieldLabelModifier = drop (length "identity")} ''Identity)

Этот код требует прагмы {-# LANGUAGE TemplateHaskell #-}.

person Christoph Hegemann    schedule 29.02.2016

Другие предоставили отличные способы изменения сгенерированных экземпляров, что иногда будет лучшим выходом. Однако это не единственный ваш выбор. У вас также есть возможность определить один или несколько типов в отдельных модулях, создать экземпляры в этих модулях, а затем импортировать уточненные модули или иным образом использовать полные имена для ссылки на перекрывающиеся имена полей.

person dfeuer    schedule 29.02.2016