Closed graingert closed 9 months ago
I don't think that's necessarily the implementation that users would expect. It assumes that the types don't overlap, i.e. that there is no value of B that when written could then be interpreted as a valid A on reading.
For example, if A was case class Employee(name: String)
, and B was case class IceCream(name: String, numCherries: Int)
, then {"name": "Sundae", "numCherries":1}
would read as Left(Employee(Sundae))
, when it should actually read as Right(IceCream(Sundae, 1))
.
I imagine it is for this reason that play-json does not provide an implicit format for eithers; it really is a decision that users need to make. In some cases users will know that their types cannot have overlapping representations, in which case your implementation may be the preferred representation. But in other cases, they will need a format that explicitly encodes whether the data should be read as an A or a B. One way to do that is as follows:
def eitherObjectFormat[A: Format, B: Format](leftKey: String, rightKey: String): Format[Either[A, B]] =
OFormat(
(__ \ rightKey).read[B].map(b => Right(b): Either[A, B]) orElse
(__ \ leftKey).read[A].map(a => Left(a): Either[A, B]),
OWrites[Either[A, B]] {
case Right(rightValue) => Json.obj(rightKey -> Json.toJson(rightValue))
case Left(leftValue) => Json.obj(leftKey -> Json.toJson(leftValue))
}
)
what about an Either reads that by default fails for ambiguous Eithers, but could be overridden by one that defaults to Right(v)
@graingert We can add the reads, but I don't think it should be provided implicitly by default.
Why not have it include the left/right in the structure?
[{
"employeeOrIceCream1": {
"Left": "Cherry"
}
},{
"employeeOrIceCream2": {
"Right": "Sundae"
}
}]
There are better ways to encode it in json I am sure. Admittedly it is less useful when consuming external data, but it makes things much clearer.
One can trivially implement Writes
but not Reads
for general Either
s, so maybe this part is worth doing.
The implementation is fairly simple: https://gist.github.com/graingert/7b1c9d20fb5f4cb081dd5a640ca335f4#file-jseither-scala
but I don't see why it's not built in.