Как сериализовать карту в JSON в Scala?

#json #scala #serialization #map #lift

#json #scala #сериализация #словарь #поднять

Вопрос:

Итак, у меня есть карта в Scala, подобная этой:

 val m = Map[String, String](
    "a" -> "theA",
    "b" -> "theB",
    "c" -> "theC",
    "d" -> "theD",
    "e" -> "theE"
)
  

и я хочу сериализовать эту структуру в строку JSON, используя lift-json.

Кто-нибудь из вас знает, как это сделать?

Ответ №1:

Если вы используете последнюю версию Scala 2.10.x и выше :

 println(scala.util.parsing.json.JSONObject(m))
  

Комментарии:

1. Это было удалено из Scala 2.11 и выше.

2. @Soid выглядит так, как будто он все еще там : scala-lang.org/api/2.11.8/scala-parser-combinators /…

3. Возможно, я где-то допустил ошибку. Но смогли ли вы ее использовать? Какую версию scala-parser-combinators вы использовали (была бы полезна полная зависимость от maven)?

4. Похоже, что он неправильно преобразуется в объект. Например, Map("0" -> 1, "1" -> List(Map("2" -> 3))) имеет строковый формат {"0" : 1, "1" : List(Map(2 -> 3))} .

5. Будьте осторожны, это не повторяет, а преобразует вложенные объекты в их строковое представление!

Ответ №2:

Как насчет этого?

 implicit val formats = net.liftweb.json.DefaultFormats
import net.liftweb.json.JsonAST._
import net.liftweb.json.Extraction._
import net.liftweb.json.Printer._
val m = Map[String, String](
    "a" -> "theA",
    "b" -> "theB",
    "c" -> "theC",
    "d" -> "theD",
    "e" -> "theE"
)
println(compact(render(decompose(m))))
  

вывод:

 {"e":"theE","a":"theA","b":"theB","c":"theC","d":"theD"}
  

Редактировать:

Для scala.collections.mutable.Map сначала вы должны преобразовать ее в неизменяемую карту: .toMap

Комментарии:

1. Когда я меняю тип на scala.collections.mutable. Карта, результат становится: [{«_1″:»d»,»_2″:»theD»},{«_1″:»a»,»_2″:»theA»},{«_1″:»c»,»_2″:»theC»},{«_1″:»b»,»_2″:»theB»},{«_1″:»e»,»_2″:»theE»}] , что делает меня немного грустным.

2. Для этого есть простое решение — просто вызовите . Чтобы получить неизменяемую карту, щелкните по изменяемой карте, и тогда все снова будет работать нормально.

Ответ №3:

Вы можете довольно легко создать свою собственную (ура, никаких зависимостей). Этот выполняет базовую обработку типов и будет выполнять рекурсию, в отличие от JSONObject упомянутого:

 import scala.collection.mutable.ListBuffer

object JsonConverter {
  def toJson(o: Any) : String = {
    var json = new ListBuffer[String]()
    o match {
      case m: Map[_,_] => {
        for ( (k,v) <- m ) {
          var key = escape(k.asInstanceOf[String])
          v match {
            case a: Map[_,_] => json  = """   key   "":"   toJson(a)
            case a: List[_] => json  = """   key   "":"   toJson(a)
            case a: Int => json  = """   key   "":"   a
            case a: Boolean => json  = """   key   "":"   a
            case a: String => json  = """   key   "":""   escape(a)   """
            case _ => ;
          }
        }
      }
      case m: List[_] => {
        var list = new ListBuffer[String]()
        for ( el <- m ) {
          el match {
            case a: Map[_,_] => list  = toJson(a)
            case a: List[_] => list  = toJson(a)
            case a: Int => list  = a.toString()
            case a: Boolean => list  = a.toString()
            case a: String => list  = """   escape(a)   """
            case _ => ;
          }
        }
        return "["   list.mkString(",")   "]"
      }
      case _ => ;
    }
    return "{"   json.mkString(",")   "}"
  }

  private def escape(s: String) : String = {
    return s.replaceAll(""" , "\\"");
  }
}
  

Вы можете увидеть это в действии следующим образом

 println(JsonConverter.toJson(
    Map("a"-> 1,
        "b" -> Map(
            "nes"ted" -> "yeah{"some":true"),
            "c" -> List(
                1,
                2,
                "3",
                List(
                    true,
                    false,
                    true,
                    Map(
                        "1"->"two",
                        "3"->"four"
                    )
                )
            )
        )
    )
)

{"a":1,"b":{"nes"ted":"yeah{"some":true"},"c":[1,2,"3",[true,false,true,{"1":"two","3":"four"}]]}
  

(Это часть библиотеки Coinbase GDAX, которую я написал, см. util.scala)

Ответ №4:

Вы можете использовать этот простой способ, если используете play Framework:

 import play.api.libs.json._

Json.toJson(<your_map>)
  

Ответ №5:

Этот код преобразует множество различных объектов и не требует никаких библиотек, кроме встроенных в scala.util.parsing.json._ . Он не будет должным образом обрабатывать граничные случаи, такие как карты с целыми числами в качестве ключей.

 import scala.util.parsing.json.{JSONArray, JSONObject}
def toJson(arr: List[Any]): JSONArray = {
  JSONArray(arr.map {
    case (innerMap: Map[String, Any]) => toJson(innerMap)
    case (innerArray: List[Any])      => toJson(innerArray)
    case (other)                      => other
  })
}
def toJson(map: Map[String, Any]): JSONObject = {
  JSONObject(map.map {
    case (key, innerMap: Map[String, Any]) =>
      (key, toJson(innerMap))
    case (key, innerArray: List[Any]) =>
      (key, toJson(innerArray))
    case (key, other) =>
      (key, other)
  })
}
  

Ответ №6:

Аналогично решению Эйнара, вы можете использовать JSONObject из Parser Combinators для этого. Обратите внимание, что это не повторяется, вам нужно будет сделать это самостоятельно. Библиотека также включает JSONArray для list-подобных структур данных. Что-то вроде следующего решит проблемы Ноэля по поводу вложенных структур. Этот пример не повторяется до произвольного уровня, но будет обрабатывать значение List[Map[String, Any]].

 import scala.util.parsing.json.{JSONArray, JSONObject}

def toJson(m : Map[String, Any]): String = JSONObject(
  m.mapValues {
    case mp: Map[String, Any] => JSONObject(mp)
    case lm: List[Map[String, Any]] => JSONArray(lm.map(JSONObject(_)))
    case x => x
    }
  ).toString
  

Ответ №7:

Дополняю ответ @Raja.

Для этих вложенных объектов я локально изменяю класс, чтобы он был моим желаемым toString() таким образом:

case class MList[T]() extends MutableList[T] {
override def toString() = "[" this.toList.mkString(",") "]"
}

Затем внутри объекта Map я использую это MList вместо стандартного List . Таким образом, мой map объект отлично печатается при вызове JSONObject(map).toString() .