Using Spray JSON, I would like to be able to parse an array of String, but still be able to deserialize correctly if a single String comes. That is, with this field:
arrayval: List[String]
and this JSON:
arrayval: ["a", "b"]
it would create a List("a","b"), and with this JSON:
arrayval: "a"
it would create a List("a"). Using default listFormat it would complain in the second case. Is there a way to configure this kind of flexibility?
In case it helps anyone, I've solved it by overriding the listFormat in the CollectionFormats (which is used in the DefaultJsonProtocol).
trait FlexibleCollectionFormats extends CollectionFormats {
implicit override def listFormat[T: JsonFormat] = new RootJsonFormat[List[T]] {
import spray.json._
def write(list: List[T]) = JsArray(list.map(_.toJson).toVector)
def read(value: JsValue): List[T] = value match {
case JsArray(elements) => elements.map(_.convertTo[T])(collection.breakOut)
case JsString(element) => List[T](new JsString(element).convertTo[T])
case x => deserializationError("Expected List as JsArray, but got " + x)
}
}
}
Then I created my own protocol instead of DefaultJsonProtocol, that basically uses the same ones as Default but overriding the CollectionFormats:
trait FlexibleDefaultJsonProtocol
extends BasicFormats
with StandardFormats
// with CollectionFormats
with FlexibleCollectionFormats
with ProductFormats
with AdditionalFormats
object FlexibleDefaultJsonProtocol extends FlexibleDefaultJsonProtocol
and later you use extends FlexibleDefaultJsonProtocol
instead of DefaultJsonProtocol. You can always switch and use one or the other in your classes, so I like the flex it provides.