Декодирование в рекурсивные АТД с аргонавтом

Я пытаюсь разобрать json как

{
  "element": "string",
  "content": "application/json"
}

где element решает, какой тип JSON. Но мой код не удается разобрать.

http://scastie.org/15213

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

}

1 ответ

Решение

Я собираюсь ответить с текущей вехой бесформенного аргонавта (1.0.0-M1), который имел соответствующие дополнения со времени 0.3.1 версия.

Это позволяет указать так называемый JsonSumCodecs для типов суммы, которые определяют, как подтипы кодируются / распознаются.

Определив один для Element в сопутствующем объекте, как

implicit val jsonSumCodecForElement = derive.JsonSumCodecFor[Element](
  derive.JsonSumTypeFieldCodec(
    typeField = "element",
    toTypeValue = Some(_.stripSuffix("Element").toLowerCase)
  )
)

ваш пример просто работает:

> Parse.decodeEither[Element](member)
res1: (String \/ (String, CursorHistory)) \/ Element =
  \/-(StringElement(application/json))

JsonSumCodecFor выше является классом типа, который обеспечивает JsonSumCodec для данного типа, здесь Element, Как JsonSumCodec, мы выбираем JsonSumTypeFieldCodec, который использует поле, "type" по умолчанию, чтобы различать подтипы. Здесь мы выбираем "element" вместо "type"и мы также преобразуем имена подтипов (toTypeValue аргумент), чтобы они совпадали с именами входных данных примера.

Другие вопросы по тегам