2016-04-08 4 views
2

Пусть у меня есть такие JSONDecode одно поле объекта в массиве JSON с Аргонавт/Цирцеи

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

и такой случай класс

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

В плей-JSON я мог бы написать читаться так :

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

Я ищу эквивалентный способ декодирования только «ша» из «родителей» в Аргонавт/Цирцеях, но я не нашел ни одного , «HCursor/ACursor» имеет downArray, но оттуда я не знаю, что делать. Заранее большое спасибо!

ответ

2

Ни цирц, ни аргонавт не отслеживает, какие поля были прочитаны в объектах JSON, поэтому вы можете просто игнорировать дополнительное поле "url" (как в Play). Более сложная часть - найти эквивалент Play's \\, которого нет на данный момент, хотя вы убедили меня, что нам нужно добавить его.

Прежде всего, это относительно легко, если у вас есть отдельный тип Sha:

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

Или, используя аппликативный синтаксис кошек'S:

import cats.syntax.cartesian._ 

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

И потом:

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

Но на самом деле это не ответ, так как я не прошу вас менять свою модель. :) Фактический ответ немного меньше удовольствия:

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

Это плохо, и мне стыдно, что это необходимо, но это работает. Я только что подал an issue, чтобы убедиться, что это улучшится в будущем выпуске circe.