3

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?

Krzysztof Atłasik
  • 21,985
  • 6
  • 54
  • 76
soosita
  • 187
  • 13
  • 1
    What do you mean by: "but when I put it into a generic method it required a to be an option due to the error:"? What generic method? Do I understand that you are changing your class because of the error you get after "putting it into a generic method"? – michaJlS Feb 20 '20 at 19:46
  • you understand correctly, im changing the class in-order to make it work, the method is convertToObj[Foo]("{}") – soosita Feb 20 '20 at 19:49

1 Answers1

1

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
Damien O'Reilly
  • 952
  • 5
  • 12
  • Thanks for the answers and note. the first example works perfectly, however the second example yields the error "could not find implicit value for parameter decoder: io.circe.Decoder[Foo]" while compiling – soosita Feb 23 '20 at 08:30
  • Strange, it is working for me. Can you double check the imports. Specifically `io.circe.generic.extras.auto._` Also the version of circe I tested with is: `0.12.2`. – Damien O'Reilly Feb 24 '20 at 13:21
  • i double checked everything..however my circe version is ```0.13.0``` – soosita Feb 24 '20 at 15:55