Error : spray.json.SerializationException: Map key must be formatted as JsString, not '["A","b"]'
ontologiae opened this issue · 7 comments
Hi, I defined an object as follow :
case class Obj(a: String, b: Map[String, List[String]], c: String, d : Int, f : Map[(String,String), (Float,Float)])
All the config for spray worked until I add Map[(String,String), (Float,Float)]
println(Obj("id", Map( "Rule1" -> List("el1","el2")), "srcRep", 1, Map( (("A","b")) -> ((0.5f,1.2f)) ) ).toJson)
Here's the stacktrace :
Exception in thread "main" spray.json.SerializationException: Map key must be formatted as JsString, not '["A","b"]'
at spray.json.CollectionFormats$$anon$3$$anonfun$write$3.apply(CollectionFormats.scala:53)
at spray.json.CollectionFormats$$anon$3$$anonfun$write$3.apply(CollectionFormats.scala:50)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
at scala.collection.immutable.Map$Map1.foreach(Map.scala:116)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
at scala.collection.AbstractTraversable.map(Traversable.scala:104)
at spray.json.CollectionFormats$$anon$3.write(CollectionFormats.scala:50)
at spray.json.CollectionFormats$$anon$3.write(CollectionFormats.scala:48)
at spray.json.ProductFormats$class.productElement2Field(ProductFormats.scala:46)
at com.xxxxxx.JsonProtocols$.productElement2Field(JsonConversion.scala:6)
at spray.json.ProductFormatsInstances$$anon$5.write(ProductFormatsInstances.scala:126)
at spray.json.ProductFormatsInstances$$anon$5.write(ProductFormatsInstances.scala:118)
at spray.json.PimpedAny.toJson(package.scala:39)
at com.xxxxxxx.MainGenere$.delayedEndpoint$com$xxxxxx$xxxxx$MainGenere$1(MainGenere.scala:415)
Tuple2 is encoded as JSON array. And JSON array could not be an JSON object key.
You can create your own JsonFormat[(String, String)] which will serialize tuple as string ("str1,str2" for example), but in this case you can't import all of DefaultJsonFormat._ because it already have deserizlier for Tuple2[T1 : JsonFormat, T2 : JsonFormat]
For me this workaround doesn't work :
object Foo extends DefaultJsonProtocol {
import spray.json._
case class CampKey(value: String) extends AnyVal
case class DataPointKey(value: String) extends AnyVal
case class DataPointValue(value: String) extends AnyVal
type DataMap = Map[CampKey, Map[DataPointKey, Set[DataPointValue]]]
implicit object dataPointKeyFormat extends RootJsonReader[DataPointKey] { def read(json: JsValue): DataPointKey = DataPointKey(json.asInstanceOf[JsString].value) }
implicit object dataPointValueFormat extends RootJsonReader[DataPointValue] { def read(json: JsValue): DataPointValue = DataPointValue(json.asInstanceOf[JsString].value) }
implicit object campKeyFormat extends RootJsonReader[CampKey] { def read(json: JsValue): CampKey = CampKey(json.asInstanceOf[JsString].value) }
implicitly[JsonReader[DataMap]] // Cannot find JsonReader or JsonFormat type class for DataMap
But JsonReader implicit is not resolved for DataMap
That would work:
object Foo {
import spray.json._
case class CampKey(value: String) extends AnyVal
case class DataPointKey(value: String) extends AnyVal
case class DataPointValue(value: String) extends AnyVal
type DataMap = Map[CampKey, Map[DataPointKey, Set[DataPointValue]]]
implicit object dataPointKeyFormat extends RootJsonFormat[DataPointKey] {
def read(json: JsValue): DataPointKey = DataPointKey(json.asInstanceOf[JsString].value)
override def write(obj: DataPointKey): JsValue = JsString(obj.value)
}
implicit object dataPointValueFormat extends RootJsonFormat[DataPointValue] {
def read(json: JsValue): DataPointValue = DataPointValue(json.asInstanceOf[JsString].value)
override def write(obj: DataPointValue): JsValue = JsString(obj.value)
}
implicit object campKeyFormat extends RootJsonFormat[CampKey] {
def read(json: JsValue): CampKey = CampKey(json.asInstanceOf[JsString].value)
override def write(obj: CampKey): JsValue = JsString(obj.value)
}
implicit val dataPointValueSetFormat = new RootJsonFormat[Set[DataPointValue]] {
def write(items: Set[DataPointValue]) = JsArray(items.map(_.toJson).toVector)
def read(value: JsValue) = value match {
case JsArray(elements) => elements.map(_.convertTo[DataPointValue]).toSet[DataPointValue]
case x => deserializationError("Expected Array as JsArray, but got " + x)
}
}
implicit val dataMapEntryFormat = DefaultJsonProtocol.mapFormat[DataPointKey, Set[DataPointValue]]
implicit val dataMapRootReader = DefaultJsonProtocol.mapFormat[CampKey, Map[DataPointKey, Set[DataPointValue]]]
}
And you should probably change reads, to get rid of asInstanceOf, and fail with DeserializationException
using deserializationError
if value is not JsString.
Yeah I did not want to discourage people from reading it as they usually give up on snippets longer that xx lines and I would decrease my changes on getting help :-)
import spray.json._
import spray.json.DefaultJsonProtocol._
val list = Map("k"->"f","n"->"r","d"->ListInt)
println(list.toJson)
compiler error: Cannot find JsonWriter or JsonFormat type class for scala.collection.immutable.Map[String,java.io.Serializable]
I used wrong?
@chenshaoxing you crossposted at #292, I answered there.