play-json icon indicating copy to clipboard operation
play-json copied to clipboard

Feature request: Support Either in Json.format

Open graingert opened this issue 7 years ago • 5 comments

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.

graingert avatar Mar 06 '17 19:03 graingert

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))
      }
)

dhpiggott avatar Mar 10 '17 21:03 dhpiggott

what about an Either reads that by default fails for ambiguous Eithers, but could be overridden by one that defaults to Right(v)

graingert avatar Mar 13 '17 11:03 graingert

@graingert We can add the reads, but I don't think it should be provided implicitly by default.

gmethvin avatar Mar 17 '17 01:03 gmethvin

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.

bigjason avatar Apr 28 '17 22:04 bigjason

One can trivially implement Writes but not Reads for general Eithers, so maybe this part is worth doing.

asavelyev01 avatar Jul 24 '19 13:07 asavelyev01