Декодирование в рекурсивные АТД с аргонавтом
Я пытаюсь разобрать json как
{
"element": "string",
"content": "application/json"
}
где element
решает, какой тип JSON. Но мой код не удается разобрать.
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
версия.
Это позволяет указать так называемый JsonSumCodec
s для типов суммы, которые определяют, как подтипы кодируются / распознаются.
Определив один для 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
аргумент), чтобы они совпадали с именами входных данных примера.