【问题标题】:Decoding into recursive ADTs with argonaut使用 argonaut 解码为递归 ADT
【发布时间】:2016-02-26 11:40:16
【问题描述】:

我正在尝试解析 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")

}

【问题讨论】:

    标签: scala argonaut


    【解决方案1】:

    我将用 argonaut-shapeless (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 参数),以使它们与示例输入的名称匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多