Search code examples
jsonscalaplayframeworkplay-json

Scala Play Json Format for Map[Locale, String]


I have objects of type

Map[java.util.Locale, String] 

How can I make Json Writes / Reads for this? I have looked at a couple other questions, but couldn't come up with a solution myself. I got (but not tested yet) something for Locale

implicit val localeReads: Reads[Locale] = new Reads[Locale] {
  def reads(json: JsValue): JsResult[Locale] =
    json match {
      case JsString(langString) => JsSuccess(new Locale(langString))
      case _ => JsError("Locale Language String Expected")
    }
}

implicit val localeWrites: Writes[Locale] = new Writes[Locale] {
  def writes(locale: Locale) = JsString(locale.toString)
}

How can I then use this in

implicit val myMapReads: Reads[Map[Locale, String]] = ???
implicit val myMapWrites: Writes[Map[Locale, String]] = ???

?


Solution

  • This should work:

    implicit val localeReads: Reads[Locale] = new Reads[Locale] {
      def reads(json: JsValue): JsResult[Locale] =
        json match {
          case JsString(langString) => JsSuccess(new Locale(langString))
          case _ => JsError("Locale Language String Expected")
        }
    }
    
    implicit val localeWrites: Writes[Locale] = new Writes[Locale] {
      def writes(locale: Locale) = JsString(locale.toString)
    }
    
    implicit val myMapWrites: Writes[Map[Locale, String]] = new Writes[Map[Locale, String]] {
      override def writes(o: Map[Locale, String]): JsValue = Json.toJson(o)
    }
    
    implicit val myMapRead: Reads[Map[Locale, String]] = new Reads[Map[Locale, String]] {
      override def reads(json: JsValue): JsResult[Map[Locale, String]] = JsSuccess {
        json.as[JsObject].value.map {
          case (k, v) => (new Locale(k), v.as[String])
        }.toMap
      }
    }
    

    Basically play already knows how to convert a Locale to json because of the Writes you provided so simply calling toJson will work.

    For the Reads it's a bit more complex and you have to do a mapping, .value returns a Map[String, JsValue] where the first represents the Locale object and the second a simple string so calling as[String] will already give you what you want.

    Note that I've wrapped everything in a JsSuccess but you may assume that the json you got cannot be converted to JsObject, apply a try/catch, and decide wether you want to return success or failure.