I'm trying to parse json like
{
"element": "string",
"content": "application/json"
}
where element
decides which type the json is. But my code fails to parse.
import scalaz._, Scalaz._
import argonaut._, Argonaut._, Shapeless._
case class ArrayAttributes(default: List[StringElement])
sealed trait Element
case class StringElement(content: String) extends Element
case class ArrayElement(attributes: ArrayAttributes, content: List[Element]) extends Element
case class Reference(element: String) extends Element { def content = element }
object Parser {
def kindDecode[T](
kinds: Map[String, DecodeJson[T]],
fail: HCursor => DecodeResult[T] = { c: HCursor => DecodeResult.fail[T]("expected one of ${kind.keys}", c.history) }): DecodeJson[T] = DecodeJson(c =>
(c --\ "element").as[String].flatMap { kind =>
kinds.get(kind).map(_.decode(c)).getOrElse(fail(c))
}
)
implicit def elementDecode: DecodeJson[Element] = kindDecode(
Map(
"string" -> DecodeJson.of[StringElement].map(identity[Element]),
"array" -> arrayDecode.map(identity[Element])
),
{ c => DecodeJson.of[Reference].decode(c).map(identity[Element]) }
)
def arrayDecode: DecodeJson[ArrayElement] = jdecode2L(ArrayElement.apply)("attributes", "content")
}
I'm going to answer with the current milestone of argonaut-shapeless (
1.0.0-M1
), which has had relevant additions since the0.3.1
version.It allows to specify so-called
JsonSumCodec
s for sum types, that drive how the sub-types are encoded / discriminated.By defining one for
Element
in its companion object, likeyour example just works:
JsonSumCodecFor
above is a type class that provides aJsonSumCodec
for a given type, hereElement
. As aJsonSumCodec
, we chooseJsonSumTypeFieldCodec
, that uses a field,"type"
by default, to discriminate between sub-types. Here, we choose"element"
instead of"type"
, and we also transform the sub-type names (toTypeValue
argument), so that these match the names of the example input.