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.