com-lihaoyi / upickle

uPickle: a simple, fast, dependency-free JSON & Binary (MessagePack) serialization library for Scala
https://com-lihaoyi.github.io/upickle
MIT License
707 stars 158 forks source link

Documentation for custom picklers for unsealed traits #562

Open malcolmredheron opened 4 months ago

malcolmredheron commented 4 months ago

It took me longer (and more help from chatgpt) than I'd like to figure out a clean way (IMO) of handling unsealed traits and other polymorphic types.

I eventually hit upon this:

      import upickle.default.{macroRW, read, write, ReadWriter as RW, readwriter}

      trait IntOrBool
      case class IntWrapper(i: Int) extends IntOrBool
      case class BoolWrapper(b: Boolean) extends IntOrBool
      given RW[IntWrapper] = macroRW[IntWrapper]
      given RW[BoolWrapper] = macroRW[BoolWrapper]
      given RW[IntOrBool] = readwriter[ujson.Value].bimap[IntOrBool](
        {
          case iw: IntWrapper => writeJs(iw).obj += ("type" -> writeJs("IntWrapper"))
          case bw: BoolWrapper => writeJs(bw).obj += ("type" -> writeJs("BoolWrapper"))
          case t => throw new IllegalArgumentException(s"Unknown type: $t")
        },
        json =>
          json("type").str match {
            case "IntWrapper" => read[IntWrapper](json)
            case "BoolWrapper" => read[BoolWrapper](json)
            case t => throw new IllegalArgumentException(s"Unknown type: $t")
          },
      )

Is there a cleaner way? How would you feel about documenting this or some other approach in the reade?