I have a data type like this:
data A = A T.Text deriving (Generic, Show)
instance A.ToJSON A
If I use A.encode
to it:
A.encode $ A "foobar" -- "foobar"
Then I use singleTagConstructors
on it:
instance A.ToJSON A where
toEncoding a = A.genericToEncoding $ A.defaultOptions { A.tagSingleConstructors = True }
A.encode $ A "foobarquux" -- "{tag: A, contents: foobarquux}"
At some point I made another data type:
newtype Wrapper a = Wrapper
{ unWrap :: a
} deriving (Show)
instance A.ToJSON a => A.ToJSON (Wrapper a) where
toJSON w = A.object [ "wrapped" A..= unWrap w ]
Here's the part where I get confused:
A.encode $ Wrapper $ A "foobar" -- "{wrapped: foobar}"
How do I get the result to be like this?
"{wrapped: {tag: A, contents: foobarquux}}"
To answer the question directly, you can always implement the Wrapper
instance with tagSingleConstructors = False
, like this:
instance Generic a => A.ToJSON (Wrapper a) where
toJSON w = A.object [ "wrapped" A..= encA (unWrap w) ]
where
encA = A.genericToEncoding $ A.defaultOptions { A.tagSingleConstructors = False }
But I don't see why you'd want to do that.
If you control the API, then you don't need the tag
field: the expected type of the wrapped value is already statically known, so tag
would not be helpful.
And if you don't control the API, I would recommend representing it very explicitly, for example as a record that exactly matches the API shape. Otherwise you run a risk of accidentally breaking the API by making unrelated changes in remote parts of the codebase.