2016-02-29 4 views
4

Если у меня есть JSON и я стараюсь, чтобы автоматически вывести FromJSON экземпляров с Generics, я бегом в проблемы с id существующим в более чем одном месте в JSON.Обработка `id` в производных экземпляров эсон FromJSON с эсонами/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 без необходимости выписывать всю инстанцию сть.

+0

Предстоящий GHC 8.0 будет иметь расширение DuplicateRecordFields https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/DuplicateRecordFields, которое облегчит эту проблему. – danidiaz

ответ

8

При выводе экземпляров 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 }) 
+0

Ницца! Я не знал, что вы можете предоставить варианты для общих экземпляров. –

+0

Это то, что мне нужно. Спасибо за помощь!. – matthias

3

Проблема не в том, что 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 #-} прагму.

2

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