If I have JSON
and I try to derive the FromJSON
instances automatically with Generics
, I run into problems with id
existing in more than one place in the JSON
.
Is there a way for me to override just the id
part or do I have to write the whole instance in order to change those particular entries? The JSON
actually has more fields, but I have left most of it out in this example. So it is actually rather tedious to write out the whole FromJSON
instance.
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
]
}
CODE So far:
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
This gives the error:
[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.
Ideally I would like to change the first id
to body_id
and the second to taxCode_id
without having to write out the whole instance.
When deriving FromJSON instances you can pass an option to the genericParseJSON
function. It's usually
data Foo = {- ... -} deriving (Show, Generic)
instance FromJSON Foo where
parseJSON = genericParseJSON defaultOptions
-- defaultOptions :: Options
while you can replace defaultOptions
with an Option
you constructed manually. The Option
type have a field fieldLabelModifier
that can preprocess the field name of your data type. You can define your data type as
data Body = Body
{ body_id :: Int
...
And write a helper function which maps "body_id"
to "id"
and anything else unchanged:
body_noprefix "body_id" = "id"
body_noprefix s = s
Then define the instance as
instance FromJSON Body where
parseJSON = genericParseJSON (defaultOptions { fieldLabelModifier = body_noprefix })
The problem isn't that GHC can't provide Generics, but that in Haskell record labels are also accessor functions and thus you get a name clash if you try to use the same label for two different records.
If you use the the functions from Data.Aeson.TH
you can use fieldLabelModifier
option to eg. remove prefixes from your labels.
data Identity = Identity
{ identitysku :: String
, identityean :: String
, identitybarcode :: String
} deriving (Show)
$(deriveJSON defaultOptions{fieldLabelModifier = drop (length "identity")} ''Identity)
This code requires the {-# LANGUAGE TemplateHaskell #-}
pragma.
Others have given great ways to modify the generated instances, which will sometimes be the best thing to do. It's not, however, your only choice. You also have the option of defining one or more of the types in separate modules, generating instances in those modules, and then importing the modules qualified or otherwise using qualified names to refer to the overlapping field names.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With