I'm using an external API which returns JSON responses. One of the responses is an array of objects and these objects are identified by the field value inside them. I'm having some trouble understanding how the parsing of such JSON response could be done with Aeson.
Here is a simplified version of my problem:
newtype Content = Content { content :: [Media] } deriving (Generic)
instance FromJSON Content
data Media =
Video { objectClass :: Text
, title :: Text } |
AudioBook { objectClass :: Text
, title :: Text }
In API documentation it is said that the object can be identified by the field objectClass which has value "video" for our Video object and "audiobook" for our AudioBook and so on. Example JSON:
[{objectClass: "video", title: "Some title"}
,{objectClass: "audiobook", title: "Other title"}]
The question is how can this type of JSON be approached using Aeson?
instance FromJSON Media where
parseJSON (Object x) = ???
You basically need a function
Text -> Text -> Media
:The
FromJSON
instance is now really simple (using<$>
and<*>
fromControl.Applicative
):However, at this point you're redundant: the
objectClass
field inVideo
orAudio
doesn't give you more information than the actual type, so you might remove it:Also note that
toMedia
is partial. You probably want to catch invalid"objectClass"
values:And last, but not least, remember that valid JSON uses strings for the name.
The default translation for a data type like:
is actually very close to what you want. (For the simplicity of my examples, I define
ToJSON
instances and encode the examples to see what kind of JSON we get.)aeson, default
So, with the default instance we have (view the complete source file which produces this output):
Let's see whether we can get even closer with custom options...
aeson, custom
tagFieldName
With custom options:
we get:
(Think yourself what you want to do with an undefined field in the real code.)
aeson, custom
constructorTagModifier
Adding
to
mediaJSONOptions
gives:Great! Exactly what you specified!
decoding
Simply add an instance with the same options to be able to decode from this format:
Example:
Complete source file.
generic-aeson, default
To get a more complete picture, let's also look at what
generic-aeson
package would give (at hackage). It has also nice default translations, different in some respects from those fromaeson
.Doing
and defining:
gives the result:
So, it's different from all what we've seen when using
aeson
.generic-aeson's options (Settings) are not interesting for us (they allow only to strip a prefix).
(The complete source file.)
aeson, ObjectWithSingleField
Apart from lower-casing the first letter of the constructor names,
generic-aeson
's translation seems similar to an option available inaeson
:Let's try this:
and yes, the result is:
the rest of options: (aeson,
TwoElemArray
)One available option for
sumEncoding
has been left out from consideration above, because it gives an array which is not quite similar to the JSON representation asked about. It'sTwoElemArray
. Example:is given by: