amnaredo / test

0 stars 0 forks source link

Can't deserialize into a field of type Option[T] #278

Open amnaredo opened 2 years ago

amnaredo commented 2 years ago

I have a JSON object where a field may not not be present, that I want to serialize into a case class.

I thought Option[T] with a default value is supposed to handle that, but that only works for me if the field is not present. If it's present, I get an exception.

Is this supposed to work?

To reproduce

~$ cat test.sc
import upickle.default._

case class Person(name: String, extra: Option[String] = None)
implicit val personRW: ReadWriter[Person] = macroRW

println("Works when field is missing")
read[Person]("""{"name": "Joe"}""")

println("Fails when field is present")
read[Person]("""{"name": "Joe", "extra": "Blow"}""")

~$ amm test.sc
Compiling /home/coverbeck/test.sc
Works when field is missing
Fails when field is present
upickle.core.AbortException: expected sequence got string at index 25
  ujson.Parser$$anonfun$reject$1.applyOrElse(Parser.scala:378)
  ujson.Parser$$anonfun$reject$1.applyOrElse(Parser.scala:374)
  scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
  ujson.Parser.rparse(Parser.scala:431)
  ujson.Parser.parse(Parser.scala:347)
  ujson.Parser.parse(Parser.scala:104)
  ujson.StringParser$.transform(StringParser.scala:29)
  ujson.StringParser$.transform(StringParser.scala:28)
  ujson.Readable$fromTransformer.transform(Readable.scala:13)
  upickle.Api.read(Api.scala:36)
  upickle.Api.read$(Api.scala:36)
  upickle.default$.read(Api.scala:126)
  ammonite.$file.test$.<clinit>(test.sc:10)
upickle.core.Abort: expected sequence got string
  upickle.core.SimpleVisitor.visitString(SimpleVisitor.scala:14)
  upickle.core.SimpleVisitor.visitString$(SimpleVisitor.scala:13)
  upickle.implicits.Readers$$anon$14.visitString(Readers.scala:170)
  ujson.Parser.rparse(Parser.scala:428)
  ujson.Parser.parse(Parser.scala:347)
  ujson.Parser.parse(Parser.scala:104)
  ujson.StringParser$.transform(StringParser.scala:29)
  ujson.StringParser$.transform(StringParser.scala:28)
  ujson.Readable$fromTransformer.transform(Readable.scala:13)
  upickle.Api.read(Api.scala:36)
  upickle.Api.read$(Api.scala:36)
  upickle.default$.read(Api.scala:126)
  ammonite.$file.test$.<clinit>(test.sc:10)

ID: 331 Original Author: coverbeck

amnaredo commented 2 years ago

I believe the problem is that Option deserialization expects an array, so try with:

read[Person]("""{"name": "Joe", "extra": ["Blow"]}""")

Original Author: darkfrog26

amnaredo commented 2 years ago

Thanks, that makes sense; it explains the error message, expected sequence got string . Unfortunately, this is wrapping the response of an external API and I can't change the schema. Original Author: coverbeck

amnaredo commented 2 years ago

The default pickler (aka upickle.default) will treat options the same way single-element collections are treated, that is as a JSON array. You can customize the pickler to work with nulls instead, as is described in the docs (example) Original Author: jodersky

amnaredo commented 2 years ago

Closing as this is intentional can can be addressed with a custom pickler. Original Author: coverbeck