In Scala, how to write an efficient json formatter for Map[IndexedSeq[String], Int]?

309 views Asked by At

I think there is not a default format for Map[IndexedSeq[String], Int] in scala (right?) So I've written my own format as follows, however it's very slow. Is there a better way to do this?

class IndexedSeqToIntMapFormat() extends Format[Map[IndexedSeq[String], Int]] {

 def writes(o: Map[IndexedSeq[String], Int]): JsValue = {
   val mapItems: Seq[String] = o.toSeq.map{case (rowKey, index) => (index.toString +: rowKey).mkString(",")}
   Json.obj("items" -> Json.toJson(mapItems))
 }

 def reads(json: JsValue): JsResult[Map[IndexedSeq[String], Int]] = {
   val mapItemsAsString: IndexedSeq[String] = (json \ "items").as[IndexedSeq[String]]
   val map: Map[IndexedSeq[String], Int] = mapItemsAsString.map(itemAsString => {
     val item: IndexedSeq[String] = itemAsString.split(",").toIndexedSeq
     val rowKey: IndexedSeq[String] = item.tail
     val rowIndex: Int = item.head.toInt
     (rowKey, rowIndex)
   }).toMap

   JsSuccess(map)
 }
}

Thanks!

1

There are 1 answers

2
doertsch On BEST ANSWER

Can't say for sure whether the following approach is significantly faster than yours, but to my understanding, it does much more conform to the "spirit" of JSON. In a JSON serialization, each object and all its sub-objects and attributes should be named, by what they are. A list of custom string serializations of complex objects is not an actual JSON representation in my opinion.

Doing it in a "proper" JSON way should at least save some time in the parsing, as it does not require additional parsing work on strings but already provides all data in the needed places. And the code looks much more readable :-)

The resulting JSON should look like this:

"items": [
  { "keySeq": [ "key1", "key2", "key3" ], "value": 42 },
  { "keySeq": [ "key4", "key5" ], "value": 123 },
  { "keySeq": [ "key6", "key7", "key7" ], "value": 650 }
]

The formatter could be something like this:

class IndexedSeqToIntMapFormat() extends Format[Map[IndexedSeq[String], Int]] {

  def writes(m: Map[IndexedSeq[String], Int]): JsValue = {
    val objs = m.toSeq.map { case (keySeq, value) =>
      Json.obj("keySeq" -> Json.toJson(keySeq), "value" -> JsNumber(value))
    }
    Json.obj("items" -> JsArray(objs))
  }

  def reads(json: JsValue): JsResult[Map[IndexedSeq[String], Int]] = {
    val seq = (json \ "items").as[Seq[JsValue]] map { obj =>
      ( (obj \ "keySeq").as[IndexedSeq[String]], (obj \ "value").as[Int] )
    }
    JsSuccess(seq.toMap)
  }
}

By the way, just out of curiosity - can you tell me in what context you need a such map?