2016-04-08 11 views
2

Angenommen, ich habe solche jsonDecode einzelnes Feld des Objekts in einem JSON-Array mit argonaut/circe

{ 
    "sha": "some sha", 
    "parents": [{ 
    "url": "some url", 
    "sha": "some parent sha" 
    }] 
} 

und einem solchen Fall Klasse

case class Commit(sha: String, parentShas: List[String]) 

Im Play-json ich das wie folgt lautet schreiben konnte :

val commitReads: Reads[Commit] = (
    (JsPath \ "sha").read[String] and 
    (JsPath \ "parents" \\ "sha").read[List[String]] 
)(Commit.apply _) 

ich die „sha“ von „Eltern“ für eine gleichwertige Art und Weise der Decodierung freu nur in argonaut/Circe, aber ich habe keine gefunden . "HCursor/ACursor" hat downArray, aber von da an weiß ich nicht, was ich tun soll. Vielen Dank im Voraus!

Antwort

2

Weder circe noch Argonaut verfolgt, welche Felder in JSON-Objekten gelesen wurden, so dass Sie einfach das zusätzliche Feld "url" ignorieren können (genau wie in Play). Der schwierigste Teil ist das Finden der Entsprechung von Play \\, die circe im Moment nicht hat, obwohl Sie mich davon überzeugt haben, dass wir es hinzufügen müssen.

Zunächst einmal ist dies relativ einfach, wenn Sie einen separaten SHA-Typ haben:

import io.circe.Decoder 

val doc = """ 
{ 
    "sha": "some sha", 
    "parents": [{ 
    "url": "some url", 
    "sha": "some parent sha" 
    }] 
} 
""" 

case class Sha(value: String) 

object Sha { 
    implicit val decodeSha: Decoder[Sha] = Decoder.instance(_.get[String]("sha")).map(Sha(_)) 
} 

case class Commit(sha: Sha, parentShas: List[Sha]) 

object Commit { 
    implicit val decodeCommit: Decoder[Commit] = for { 
    sha <- Decoder[Sha] 
    parents <- Decoder.instance(_.get[List[Sha]]("parents")) 
    } yield Commit(sha, parents) 
} 

Oder Catss applicative Syntax:

import cats.syntax.cartesian._ 

implicit val decodeCommit: Decoder[Commit] = 
    (Decoder[Sha] |@| Decoder.instance(_.get[List[Sha]]("parents"))).map(Commit(_, _)) 

Und dann:

scala> import io.circe.jawn._ 
import io.circe.jawn._ 

scala> decode[Commit](doc) 
res0: cats.data.Xor[io.circe.Error,Commit] = Right(Commit(Sha(some sha),List(Sha(some parent sha)))) 

Aber das ist nicht wirklich eine Antwort, da ich dich nicht bitten werde, dein Modell zu ändern. Die eigentliche Antwort :) ist ein bisschen weniger Spaß:

case class Commit(sha: String, parentShas: List[String]) 

object Commit { 
    val extractSha: Decoder[String] = Decoder.instance(_.get[String]("sha")) 

    implicit val decodeCommit: Decoder[Commit] = for { 
    sha <- extractSha 
    parents <- Decoder.instance(c => 
     c.get("parents")(Decoder.decodeCanBuildFrom[String, List](extractSha, implicitly)) 
    ) 
    } yield Commit(sha, parents) 
} 

Das ist schlecht, und ich schäme mich, es ist notwendig, aber es funktioniert. Ich habe gerade an issue eingereicht, um sicherzustellen, dass dies in einer zukünftigen circe-Version besser wird.