Decoding refract with Argonaut

407 Views Asked by At

I'm trying to decode json in refract-style with argonaut.

Scastie: http://scastie.org/15196

Error: Attempt to decode value on failed cursor.: [*.--(meta)]

I don't see why it wouldn't work, everything on meta is optional, even Meta itself.

Sample JSON:

{
  "element": "member",
  "content": {
    "key": {
      "element": "string",
      "content": "Content-Type"
    },
    "value": {
      "element": "string",
      "content": "application/json"
    }
  }
}

Code so far:

/***
scalaVersion := "2.11.7"
  val argonautVersion = "6.1"
  val scalazVersion = "7.1.6"
      libraryDependencies ++= Seq(
            "io.argonaut" %% "argonaut" % argonautVersion
          , "org.scalaz" %% "scalaz-core" % scalazVersion
          , "com.github.alexarchambault" %% s"argonaut-shapeless_$argonautVersion" % "0.3.1"
          , "org.scalatest" %% "scalatest" % "2.2.4" % "test"
      )


*/


import scala.language.existentials
import scalaz._, Scalaz._
import argonaut._, Argonaut._, Shapeless._

case class Document(content: Seq[Int])

case class Meta(description: Option[String], classes: Seq[String], id: Option[String])

case class Member(meta: Option[Meta], attributes: Option[MemberAttributes], content: KeyValue)
case class MemberAttributes(typeAttributes: Seq[String], default: Seq[StringElement])
case class KeyValue(key: StringElement, value: Element[_])
sealed trait Element[T] {
  def content: T
}
case class StringElement(content: String) extends Element[String]
case class ArrayElement(content: Seq[StringElement], attributes: MemberAttributes) extends Element[Seq[StringElement]]

object Parser {
  def validateElement(name: String): HCursor => Boolean = { c =>
    (c --\ "element").as[String].toDisjunction.fold(_ => false, _ == name)
  }
  def decodeAndValidate[T: DecodeJson](name: String): DecodeJson[T] =
    DecodeJson.of[T].validate(validateElement(name), s"element name should be $name")

  def elementDecode: DecodeJson[Element[_]] = DecodeJson(c =>
    (c --\ "element").as[String].flatMap { kind =>
      kind match {
        case "string" => DecodeJson.of[StringElement].decode(c).map({ x => x: Element[_] })
        case "array"  => DecodeJson.of[ArrayElement].decode(c).map({ x => x: Element[_] })
        case _        => DecodeResult.fail("expected string or array as member value", c.history)
      }
    }
  )
  def stringDecode: DecodeJson[StringElement] = decodeAndValidate[StringElement]("string")
  implicit def keyvalueDecode: DecodeJson[KeyValue] = jdecode2L(KeyValue.apply)("key", "value")(stringDecode, elementDecode)

  def documentDecode: DecodeJson[Document] = decodeAndValidate[Document]("parseResult")
  def memberDecode: DecodeJson[Member] = decodeAndValidate[Member]("member")
}

object Decoders {
  implicit def documentDecode: DecodeJson[Document] = Parser.documentDecode
  implicit def memberDecode: DecodeJson[Member] = Parser.memberDecode
}
object Main extends App {

  import Decoders._

  val shortMember = """
{
  "element": "member",
  "content": {
    "key": {
      "element": "string",
      "content": "Content-Type"
    },
    "value": {
      "element": "string",
      "content": "application/json"
    }
  }
}
"""

  println(Parse.decodeEither[Member](shortMember))

}
1

There are 1 best solutions below

0
On BEST ANSWER

argonaut-shapeless has a different opinion on Option. After manually specifying the decoders for case classes with Option, it works as expected.