Update case class from incomplete JSON with Argonaut or Circe

这一生的挚爱 提交于 2019-12-05 17:43:10

问题


I need to create an updated instance from a case class instance (with any needed DecodeJsons implicitly derived), given an incomplete json (some fields missing). How can this be accomplished with Argonaut (preferably) or Circe (if I have to)?

Example:

case class Person(name:String, age:Int)
val person = Person("mr complete", 42)
val incompletePersonJson = """{"name":"mr updated"}"""
val updatedPerson = updateCaseClassFromIncompleteJson(person, incompletePersonJson)

println(updatedPerson)
//yields Person(mr updated, 42) 

I'm pretty sure I have to parse the json to json AST, then convert it to Shapeless LabelledGeneric, then use Shapeless update somehow to update the case class instance.


Edit 2

After reading the Shapeless source I found that I can generate my own "Default"-object. I managed to create a solution which requires the instance of the case class to be present while parsing the json. I was hoping to avoid this and instead provide the instance later. Anyway here it is:

import shapeless._
import argonaut._
import ArgonautShapeless._
import shapeless.ops.hlist.Mapper

case class Person(name: String, age: Int)

object MkDefault {

  object toSome extends Poly1 {
    implicit def default[P] = at[P](Some(_))
  }

  def apply[P, L <: HList, D <: HList]
  (p: P)
  (implicit
   g: Generic.Aux[P, L],
   mpr: Mapper.Aux[toSome.type, L, D]
  ): Default.Aux[P, mpr.Out] =
    Default.mkDefault[P, D](mpr(g.to(p)))
}


object Testy extends App {
    implicit val defs0 = MkDefault(Person("new name? NO", 42))
    implicit def pd = DecodeJson.of[Person]
    val i = """{"name":"Old Name Kept"}"""
    val pp = Parse.decodeOption[Person](i).get
    println(pp)
}

This yields Person(Old Name Kept,42).


回答1:


For the sake of completeness: support for "patching" instances like this has been provided in circe since the 0.2 release:

import io.circe.jawn.decode, io.circe.generic.auto._

case class Person(name: String, age: Int)

val person = Person("mr complete", 42)
val incompletePersonJson = """{"name":"mr updated"}"""

val update = decode[Person => Person](incompletePersonJson)

And then:

scala> println(update.map(_(person)))
Right(Person(mr updated,42))

My original blog post about this technique uses Argonaut (mostly since I wrote it a couple of months before I started working on circe), and that implementation is available as a library, although I've never published it anywhere.




回答2:


You can generate those implicit val defs / pd with macro annotation on Person (in object Person, for example, and do import Person._ to summon implicits). See this unfinished Simulacrum in scalameta (scala-reflect is fine too, but seems like scalameta can be enough here) for usage examples. Also you have to specify missing default value (42) somewhere, for example, in case class constructor (age: Int = 42, recognition can be done in macro too).



来源:https://stackoverflow.com/questions/39308827/update-case-class-from-incomplete-json-with-argonaut-or-circe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!