4

I have scala class like class A(b:Seq[String])

My problem is when I deserialize it from text which have no b field my class contains null. It is possible to force deserealizer to fill with empty Seq?

I use com.fasterxml.jackson.databind.ObjectMapper with com.fasterxml.jackson.module.scala.DefaultScalaModule.

EDIT: I want solution that fix all such fields without explicitly mentioning full list of them. Ir changing all declarations.

talex
  • 17,973
  • 3
  • 29
  • 66

3 Answers3

5

This is not currently supported by Jackson, unfortunately.

You can see the relevant GitHub ticket here: https://github.com/FasterXML/jackson-databind/issues/347

Your best bet is to map null into an empty Seq in the class constructor or in an accessor method:

class A(_b: Seq[String]) {
  val b = _b match {
    case null => Nil
    case bs => bs
  }
}

(See also https://stackoverflow.com/a/20655330/8261 for other options)

Community
  • 1
  • 1
Rich
  • 15,048
  • 2
  • 66
  • 119
  • This solution require to make changes for each field of type `Seg[?]`. I want to fix problem in one place. – talex Oct 06 '15 at 11:56
1

If you use Spray JSON, then a simple example which doesn't handle the absence of the b field would look like:

import spray.json._

case class A(b: Seq[String])

object Protocol extends DefaultJsonProtocol {
  implicit def aFormat = jsonFormat1(A)
}

import Protocol._

val str1 = """{ "b" : [] }""""
val str2 = """{ "b" : ["a", "b", "c"] }""""
val str3 = """{}"""
str1.parseJson.convertTo[A]//successful
str2.parseJson.convertTo[A]//successful
str3.parseJson.convertTo[A]//Deserialization error

In Spray JSON, this can be solved by writing a more detailed protocol format for class A:

import spray.json._

case class A(b: Seq[String])

object Protocol extends DefaultJsonProtocol {

  implicit object aFormat extends RootJsonFormat[A] {
    override def write(obj: A): JsValue = JsObject("b" -> obj.b.toJson)

    override def read(json: JsValue): A = json match {
      //Case where the object has exactly one member, 'b'
      case JsObject(map) if map.contains("b") && map.size == 1 => A(map("b").convertTo[Seq[String]])
      //Case where the object has no members
      case JsObject(map) if map.isEmpty => A(Seq())
      //Any other json value, which cannot be converted to an instance of A
      case _ => deserializationError("Malformed")
    }
  }
}

import Protocol._

val str1 = """{ "b" : [] }""""
val str2 = """{ "b" : ["a", "b", "c"] }""""
val str3 = """{}"""
str1.parseJson.convertTo[A]//successful
str2.parseJson.convertTo[A]//successful
str3.parseJson.convertTo[A]//successful
mattinbits
  • 10,370
  • 1
  • 26
  • 35
  • 1
    This solution requires explicitly specify all fields of `Seq[?]` type. I have lot of them and want all of them behave this way. – talex Oct 06 '15 at 11:54
  • Can you give a concrete example? Is it many case classes or one class with many members of type seq? – mattinbits Oct 06 '15 at 12:11
  • Both of it. I have several case classes with multiple `Seq` field in each. – talex Oct 06 '15 at 14:16
  • What if I have `case class A(b: Seq[String])` and `case class B(c: Seq[String])` and I am trying to deserialize `{ }` how would I know which case class to target? – mattinbits Oct 06 '15 at 14:42
  • I don't know how jackson handle this situation, but how it relates to my question? Same problem appear even without `Seq` field – talex Oct 07 '15 at 13:05
  • It relates because the way you want to handle that situation affects the solution. For example if you know which class you are trying to deserialize to each time, situation is easier. – mattinbits Oct 07 '15 at 13:08
  • Yes I know what class I desalinize – talex Oct 07 '15 at 13:57
  • If you're able to alter your case classes, the simplest solution is to use `case class A(b: Option[Seq[String]])` then Spray Json will handle the situation no problem. – mattinbits Oct 08 '15 at 13:41
  • If i should change all declarations I better change it right way. Like Rich suggest – talex Oct 08 '15 at 16:08
  • In Scala the idiomatic way of handling data is using Option, not using null. Also for this kind of data structure, a case class would be more idiomatic for scala. With Rich's method you lose some of the benefits of case classes such as generated apply and unapply methods – mattinbits Oct 08 '15 at 16:18
  • Meant to say, "handling Optional data" – mattinbits Oct 08 '15 at 16:19
  • I agree, but I already have `Seq` so no `null` expected. My actual problem is that i got `null` and I want empty `Seq` – talex Oct 08 '15 at 17:27
0

I had a similar problem deserializing String arrays with jackson, and found that jackson apparently (have not tried yet) fixed this in jackson-module 2.1.2. It might work for Seq. https://github.com/FasterXML/jackson-module-scala/issues/48

leoconco
  • 253
  • 3
  • 15