Consider:
import io.circe.generic.auto._, io.circe.syntax._
sealed trait Data
case class Failed(foo: String, bar: String) extends Data
case class Success(foo1:String, bar1:String) extends Data
case class Task(Foo:String, Data: Data)
val something = Task("test", Failed("1", "2"))
println(something.asJson)
This outputs:
val something: Task = Task(test,Failed(1,2))
{"Foo" : "test", "Data" : {"Failed" : {"foo" : "1","bar" : "2"}}}
But what I really want, is it to output:
{"Foo" : "test", "Data" : {"foo" : "1", "bar" : "2"}}
Effectively, I just want to delete the "Failed" block but keep everything within that block.
Build info:
val scalaVer = "2.13.8"
lazy val circeJsonSchemaVersion = "0.2.0"
lazy val circeVersion = "0.14.3"
lazy val circeOpticsVersion = "0.14.1"
"io.circe" %% "circe-json-schema" % circeJsonSchemaVersion,
"io.circe" %% "circe-core" % circeVersion,
"io.circe" %% "circe-generic" % circeVersion,
"io.circe" %% "circe-parser" % circeVersion,
"io.circe" %% "circe-literal" % circeVersion,
"io.circe" %% "circe-generic-extras" % circeVersion,
"io.circe" %% "circe-optics" % circeOpticsVersion,
I have tried using #JsonCodec but wasn't able to get it working. I looked at custom codecs but that seems like it could be a giant rabbit hole.
EDIT: Fixed bad copy/paste in output
A more generic solution recommends
import GenericDerivation.{ decodeEvent => _, encodeEvent => _ }
object ShapesDerivation {
import io.circe.shapes // "io.circe" %% "circe-shapes" % ...
import shapeless.{ Coproduct, Generic }
implicit def encodeAdtNoDiscr[A, Repr <: Coproduct](implicit
gen: Generic.Aux[A, Repr],
encodeRepr: Encoder[Repr]
): Encoder[A] = encodeRepr.contramap(gen.to)
//...
}
but this seems to be outdated.
You can try to derive encoders for non-labelled coproducts manually yourself (i.e. coproducts of the form Failed :+: Success :+: CNil rather than FieldType[Symbol ## "Failed", Failed] :+: FieldType[Symbol ## "Success", Success] :+: CNil)
import io.circe.Encoder
import shapeless.{:+:, CNil, Coproduct, Generic, Inl, Inr}
trait NoLabelCoproductEncoders {
implicit def noLabelEnc[A, Repr <: Coproduct](implicit
gen: Generic.Aux[A, Repr],
enc: NoLabelCoproductEncoder[Repr]
): Encoder[A] = enc.contramap(gen.to)
}
trait NoLabelCoproductEncoder[A <: Coproduct] extends Encoder[A]
object NoLabelCoproductEncoder {
implicit def ccons[H, T <: Coproduct](implicit
hEnc: Encoder[H],
tEnc: NoLabelCoproductEncoder[T]
): NoLabelCoproductEncoder[H :+: T] = {
case Inl(h) => hEnc(h)
case Inr(t) => tEnc(t)
}
implicit val cnil: NoLabelCoproductEncoder[CNil] = _.impossible
}
This works with semiauto
import io.circe.generic.semiauto
sealed trait Data
object Data extends NoLabelCoproductEncoders
case class Failed(foo: String, bar: String) extends Data
object Failed {
implicit val failedEnc: Encoder[Failed] = semiauto.deriveEncoder[Failed]
}
case class Success(foo1:String, bar1:String) extends Data
object Success {
implicit val successEnc: Encoder[Success] = semiauto.deriveEncoder[Success]
}
case class Task(Foo:String, Data: Data)
object Task {
implicit def taskEnc: Encoder[Task] = semiauto.deriveEncoder
}
import io.circe.syntax._
val something = Task("test", Failed("1", "2"))
something.asJson.noSpaces
// {"Foo":"test","Data":{"foo":"1","bar":"2"}}
and with auto
sealed trait Data
object Data extends NoLabelCoproductEncoders
case class Failed(foo: String, bar: String) extends Data
case class Success(foo1:String, bar1:String) extends Data
case class Task(Foo:String, Data: Data)
import io.circe.generic.auto._
import Data._ // to make noLabelEnc of higher priority for Data, otherwise it's ambiguous with Circe auto._ encoders (in LowPriorityEncoders)
import io.circe.syntax._
val something = Task("test", Failed("1", "2"))
something.asJson.noSpaces
// {"Foo":"test","Data":{"foo":"1","bar":"2"}}
Related
I have a need to serialize a handful of case classes to only strings and ints. Meaning, if there's a nested type, it gets serialized as the stringified version of a JSON object, not a JSON object.
Example:
case class Deepest(someNum: Int)
case class Inner(superDeep: Deepest)
case class Outer(aValue: Int, aNestedValue: Inner)
Serializing an instance of Outer Would result in (or something similar)
{
"Outer": {
"aValue": 5,
"aNestedValue": "{ \"superDeep\": .... }"
}
}
Is this possible?
Using jsoniter-scala is the simplest and most efficient way to do it with Scala.
Add dependencies:
libraryDependencies ++= Seq(
// Use the %%% operator instead of %% for Scala.js
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.12.0",
// Use the "provided" scope instead when the "compile-internal" scope is not supported
"com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.12.0" % "compile-internal"
)
Use a custom codec from this snippet:
import com.github.plokhotnyuk.jsoniter_scala.macros._
import com.github.plokhotnyuk.jsoniter_scala.core._
object Example01 {
sealed trait Message
case class Deepest(someNum: Int)
case class Inner(superDeep: Deepest)
case class Outer(aValue: Int, aNestedValue: Inner) extends Message
implicit val innerCodec: JsonValueCodec[Inner] = new JsonValueCodec[Inner] {
private val codec: JsonValueCodec[Inner] = JsonCodecMaker.make
override def decodeValue(in: JsonReader, default: Inner): Inner =
readFromStringReentrant(in.readString(null))(codec)
override def encodeValue(x: Inner, out: JsonWriter): Unit =
out.writeVal(writeToStringReentrant(x)(codec))
override def nullValue: Inner = null
}
implicit val messageCodec: JsonValueCodec[Message] =
JsonCodecMaker.make(CodecMakerConfig.withDiscriminatorFieldName(None))
def main(args: Array[String]): Unit = {
val message = readFromString[Message](
"""{
| "Outer": {
| "aValue": 5,
| "aNestedValue": "{ \"superDeep\": { \"someNum\": 1 } }"
| }
|}""".stripMargin)
println(writeToString[Message](message, WriterConfig.withIndentionStep(4)))
}
}
Expected output:
{
"Outer": {
"aValue": 5,
"aNestedValue": "{\"superDeep\":{\"someNum\":1}}"
}
}
If more than one type should be stringified you can define a function that takes a regular codec and return a codec that stringify output of original one:
def makeStringifyingCodec[A](codec: JsonValueCodec[A]): JsonValueCodec[A] =
new JsonValueCodec[A] {
override def decodeValue(in: JsonReader, default: A): A =
readFromStringReentrant(in.readString(null))(codec)
override def encodeValue(x: A, out: JsonWriter): Unit =
out.writeVal(writeToStringReentrant(x)(codec))
override def nullValue: A = null.asInstanceOf[A]
}
And use it for field types of the Outer class:
implicit val innerCodec: JsonValueCodec[Inner] =
makeStringifyingCodec(JsonCodecMaker.make[Inner])
implicit val messageCodec: JsonValueCodec[Message] =
JsonCodecMaker.make(CodecMakerConfig.withDiscriminatorFieldName(None))
I have encountered a weird situation.
I m trying to build a method that takes a type and a JSON
and build it into a case class instance and if needed auto-complete missing key values.
So far I managed to do everything separately but not altogether.
The case class with its defaults:
case class Foo(a: String = "empty String", b: Option[Int] = Some(1))
and when I do the conversion:
import io.circe.generic.extras.auto._
import io.circe.generic.extras.Configuration
import io.circe.parser.decode
implicit val customConfig: Configuration = Configuration.default.withDefaults
println(decode[Foo]("{}"))
this is the output I get:
Right(Foo(empty String,Some(1)))
and this is working as I expected
but when I put it into a generic method it required a to be an option due to the error:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: DecodingFailure(Attempt to decode value on failed cursor, List(DownField(a)))
so I`m changing the case class to be
case class Foo(a: Option[String] = Some("empty String"), b: Option[Int] = Some(1))
and add the decoder:
object Foo{
implicit val decoder:Decoder[Foo] = deriveDecoder[Foo]
}
to the method:
import io.circe.Decoder
import io.circe.parser.decode
def convertToObj[T](jsonStr: String)(implicit decoder: Decoder[T]): T = {
decode[T](jsonStr)
match {
case Right(value) => value
case Left(error) => throw error
}
}
println(convertToObj[Foo]("{}"))
and the output is:
Foo(None,None)
so now I have lost my default values that I put and not able to use the automatic decoder as well.
How can I combine my two wishes into one approach?
You would need to do something like:
package foo.bar
import io.circe.Decoder
import io.circe.generic.extras.semiauto
import io.circe.generic.extras.Configuration
import io.circe.parser.decode
case class Foo(a: String = "empty String", b: Option[Int] = Some(1))
object Foo {
implicit val customConfig: Configuration = Configuration.default.withDefaults
implicit val decoder: Decoder[Foo] = semiauto.deriveConfiguredDecoder[Foo]
}
object TestApp extends App {
def convertToObj[T](jsonStr: String)(implicit decoder: Decoder[T]): T =
decode[T](jsonStr) match {
case Right(value) => value
case Left(error) => throw error
}
println(convertToObj[Foo]("{}"))
}
However, you can have circe automatically derive your decoder for you, so you can get away with less boilerplate:
package foo.bar
import io.circe.Decoder
import io.circe.generic.extras.auto._
import io.circe.generic.extras.Configuration
import io.circe.parser.decode
case class Foo(a: String = "empty String", b: Option[Int] = Some(1))
object TestApp extends App {
implicit val customConfig: Configuration = Configuration.default.withDefaults
def convertToObj[T](jsonStr: String)(implicit decoder: Decoder[T]): T =
decode[T](jsonStr) match {
case Right(value) => value
case Left(error) => throw error
}
println(convertToObj[Foo]("{}"))
}
Both of these examples give me output: Foo(empty String,Some(1))
NOTE:
method deriveDecoder in object semiauto is deprecated (since 0.12.0): Use deriveConfiguredDecoder
Experimenting with Circe to serialize and deserialize Algebraic Data Types in Scala I tried out the following sample, following the doc and some samples on the web:
sealed trait StringData
case class Data2(f1: String, f2: String) extends StringData
case class Data3(f1: String, f2: String, f3: String) extends StringData
object StringData {
// ===> does not work, always picks Data2 type
implicit val decodeData: Decoder[Data] = Decoder[OptionsData].map[Data](identity).or(Decoder[TextData].map[Data](identity))
implicit val encodeData: Encoder[StringData] = Encoder.instance {
case d2 # Data2( _,_) => d2.asJson
case d3 # Data3( _, _, _) => d3.asJson
}
def toJson(s: StringData): String = s.asJson.noSpaces
def fromJson(s: String): Either[Error, StringData] = decode[StringData](s)
}
"Inheritance ADT with identical fields" should "serialize and deserialize with Circe" in {
val d2 = Data2("a", "b")
val d3 = Data3("1", "2", "3")
val jd2 = StringData.toJson(d2)
val jd3 = StringData.toJson(d3)
val d2Decoded = StringData.fromJson(jd2)
val d3Decoded = StringData.fromJson(jd3)
d2Decoded.right.get should equal(d2)
d3Decoded.right.get should equal(d3)
println("")
}
The problem is that the type of d3Decoded is always of type Data2 and not the desired Data3.
The solution I came up with was to replace the decoder with this one:
implicit val decodeData: Decoder[StringData] = Decoder.instance { c =>
c.downField("f3").as[String] match {
case m: Either[DecodingFailure, String] if m.isLeft => c.as[Data2]
case m: Either[DecodingFailure, String] if m.isRight => c.as[Data3]
}
}
This seems to me a rather ad-hoc solution. In Jackson there is the possibility to add the type to the Json. I wonder if I use Circe the right way or if this is really the way to do. Any comments very welcome.
Try using discriminator documented here:
import io.circe.generic.extras.auto._
import io.circe.generic.extras.Configuration
import io.circe.parser.decode
implicit val genDevConfig: Configuration =
Configuration.default.withDiscriminator("what_am_i")
sealed trait StringData
case class Data2(f1: String, f2: String) extends StringData
case class Data3(f1: String, f2: String, f3: String) extends StringData
decode[StringData]("""{ "f1": "foo", "f2": "bar", "f3": "qux", "what_am_i": "Data3" }""")
which outputs
res0: Either[io.circe.Error,StringData] = Right(Data3(foo,bar,qux)
where
libraryDependencies ++= Seq(
"io.circe" %% "circe-core",
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser",
"io.circe" %% "circe-generic-extras",
).map(_ % circeVersion)
For a case class Apple(color:String, sweetness:Double) I can define a Decoder[String => Apple] via generic.(semi)auto or generic.extras.(semi)auto,
However for a sealed trait hierarchy (an ADT) I cannot:
sealed trait Fruit {
def color:String
}
case class Apple(color:String, sweetness:Double) extends Fruit
sealed trait SpecialFruit extends Fruit
case class Camachile(color:String, burstyness:Double) extends SpecialFruit
case class Langsat(color:String, transparency:Double) extends SpecialFruit
Decoder[String => Fruit] // <--- wont compile
How do I create such a Decoder?
Update
The reason I need such a decoder, is because
- The json I'm parsing does not contain all fields.
- The obtaining a decoder for the missing fields is non-trivial.
The last point makes it infeasible to go via Decoder[Fruit]
With decode[Fruit](jsonString), here is the example:
https://scalafiddle.io/sf/jvySm0B/0
There is a similar example on circe's home page: https://circe.github.io/circe/
Here is my attempt at an implementation. Warning: it's probably a bit off the coding standards in Circe, but it seems to work alright for my purposes. Nested sealed traits are also supported.
package no.kodeworks.kvarg.json
import io.circe.generic.extras.Configuration
import io.circe.{Decoder, HCursor}
import no.kodeworks.kvarg.util._
import shapeless.ops.function.FnFromProduct
import shapeless.ops.union.UnzipFields
import shapeless.{Coproduct, HList, LabelledGeneric, _}
trait AdtConfiguredIncompleteDecoders {
implicit def decodeIncompleteAdt[
Missing <: HList
, Adt
, Func
, Subtypes <: Coproduct
, SubtypeKeys <: HList
, SubtypeValues <: Coproduct
, ParamsSubtypes <: HList
, SubtypeFuncs <: HList
]
(implicit
func: FnFromProduct.Aux[Missing => Adt, Func],
sub: LabelledGeneric.Aux[Adt, Subtypes],
uz: UnzipFields.Aux[Subtypes, SubtypeKeys, SubtypeValues],
subtypeFuncs: SubtypeFunc.Aux[Missing, SubtypeValues, SubtypeFuncs],
configuration: Configuration = null
): Decoder[Func] = {
val conf = Option(configuration).getOrElse(Configuration.default)
val disc = conf.discriminator.getOrElse("type")
val keys = hlistToList[Symbol](uz.keys()).map(_.name)
.map(conf.transformConstructorNames)
val subtypeFuncs0 = hlistToList[Decoder[Func]](subtypeFuncs())
Decoder.withReattempt {
case h: HCursor =>
h.downField(disc).as[String] match {
case Right(decodedType) =>
val subtypeFuncDecoder = subtypeFuncs0(keys.indexOf(decodedType))
subtypeFuncDecoder(h)
}
}
}
trait SubtypeFunc[P <: HList, A <: Coproduct] extends DepFn0 with Serializable {
type Out <: HList
}
object SubtypeFunc {
type Aux[P <: HList, A <: Coproduct, Out0 <: HList] = SubtypeFunc[P, A] {
type Out = Out0}
implicit def cnilSubtypeFunc[P <: HList]: Aux[P, CNil, HNil] = new SubtypeFunc[P, CNil] {
type Out = HNil
override def apply(): HNil = HNil
}
implicit def cconsSubtypeFunc[
Missing <: HList
, CaseClass
, Func
, Rest <: Coproduct]
(implicit
func: FnFromProduct.Aux[Missing => CaseClass, Func],
subtypefuncHead: Decoder[Func],
subtypeFuncRest: SubtypeFunc[Missing, Rest],
): SubtypeFunc.Aux[Missing, CaseClass :+: Rest, Decoder[Func] :: subtypeFuncRest.Out] = {
new SubtypeFunc[Missing, CaseClass :+: Rest] {
type Out = Decoder[Func] :: subtypeFuncRest.Out
override def apply() =
subtypefuncHead :: subtypeFuncRest()
}
}
}
}
Snip from no.kodeworks.kvarg.util package object:
def hlistToList[T](hlist: shapeless.HList): List[T] = {
import shapeless._
hlist match {
case HNil => Nil
case head :: tail =>
collection.immutable.::(head.asInstanceOf[T], hlistToList[T](tail))
}
}
I'm using my own implicit implementation of JSON serializer and deserializer for my case object
My case class looks like (it's just a code snippet)
sealed trait MyTrait
case object MyCaseClass extends MyTrait
I want to write my own ser. and deser. of JSON for MyTrait
implicit val myTraitFormat = new JsonFormat[MyTrait] {
override def read(json: JsValue): MyTrait = json.asJsObject.getFields("value") match {
case Seq(JsString("TEST")) ⇒ MyCaseClass
case _ ⇒ throw new DeserializationException(s"$json is not a valid extension of my trait")
}
override def write(myTrait: MyTrait): JsValue = {
myTrait match {
case MyCaseClass => JsObject("value" -> JsString("TEST"))
}
}
}
Now my test is failing by throwing DeserializationException:
"The my JSON format" when {
"deserializing a JSON" must {
"return correct object" in {
val json = """{"value": "TEST"}""".asJson
json.convertTo[MyTrait] must equal (MyCaseClass)
}
}
}
Obviously json.asJsObject.getFields("value")can not be matched to Seq(JsString("TEST")). Maybe this is related to using traits?
But I have found example on official spray-json site https://github.com/spray/spray-json#providing-jsonformats-for-other-types
Any ideas how to properly match on field in JsObject?
Thanks!
Best
Write variable name instead of string:
override def read(json: JsValue): MyCaseClass = json.asJsObject.getFields("value") match {
case Seq(JsString(value)) ⇒ MyCaseClass
case _ ⇒ throw new DeserializationException(s"$json is not a valid case class")
}
Does that work for you?
Update:
Yes, your test is wrong. I tried it with the following (note parseJson instead of asJson) and it worked:
scala> val json = """{"value": "TEST"}""".parseJson
json: spray.json.JsValue = {"value":"TEST"}
scala> json.convertTo[MyCaseClass]
res2: MyCaseClass = MyCaseClass()
Update 2: Tried it with trait:
scala> import spray.json._
import spray.json._
scala> import spray.json.DefaultJsonProtocol._
import spray.json.DefaultJsonProtocol._
[Your code]
scala> val json = """{"value": "TEST"}""".parseJson
json: spray.json.JsValue = {"value":"TEST"}
scala> json.convertTo[MyTrait]
res1: MyTrait = MyCaseClass