Преобразование Json в карту [String, String]

Я ввел json как

{"a": "x", "b": "y", "c": "z", .... }

Я хочу преобразовать этот json в карту типа Map [String, String]

так что в основном карта пар ключ-значение.

Как я могу это сделать с помощью Circe?

Обратите внимание, что я не знаю, какие ключи «a», «b», «c» будут присутствовать в Json. Все, что я знаю, это то, что они всегда будут строками, а не каким-либо другим типом данных.

Я посмотрел на пользовательские декодеры здесь https://circe.github.io/circe/codecs/custom-codecs.html, но они работают, только если вы знаете имена тегов.

Я нашел такой пример в Джексоне. но не в кругу

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.fasterxml.jackson.databind.ObjectMapper

val data = """
    {"a": "x", "b", "y", "c": "z"}
"""
val mapper = new ObjectMapper
mapper.registerModule(DefaultScalaModule)
mapper.readValue(data, classOf[Map[String, String]])

person Knows Not Much    schedule 05.01.2019    source источник


Ответы (2)


Хотя решения в другом ответе работают, они намного более подробны, чем необходимо. Стандартный circe предоставляет неявный экземпляр Decoder[Map[String, String]], поэтому вы можете просто написать следующее:

scala> val doc = """{"a": "x", "b": "y", "c": "z"}"""
doc: String = {"a": "x", "b": "y", "c": "z"}

scala> io.circe.parser.decode[Map[String, String]](doc)
res0: Either[io.circe.Error,Map[String,String]] = Right(Map(a -> x, b -> y, c -> z))

Экземпляр Decoder[Map[String, String]] определен в Decoder сопутствующем объекте, поэтому он всегда доступен - вам не нужны никакие импорты, другие модули и т. Д. Circe предоставляет экземпляры, подобные этому, для большинства стандартных типов библиотек с разумными экземплярами. Если вы хотите, например, декодировать массив JSON в List[String], вам не нужно создавать свой собственный Decoder[List[String]] - вы можете просто использовать массив в неявной области видимости, исходящий от сопутствующего объекта Decoder.

Это не просто менее подробный способ решения этой проблемы, это рекомендуемый способ ее решения. Создание явного экземпляра декодера вручную и преобразование из Either в Try для составления операций синтаксического анализа и декодирования необязательно и подвержено ошибкам (если вам действительно нужно получить Try или Option или что-то еще, почти наверняка лучше сделать это в конце ).

person Travis Brown    schedule 05.01.2019

Предполагая:

val rawJson: String = """{"a": "x", "b": "y", "c": "z"}"""

Это работает:

import io.circe.parser._

val result: Try[Map[String, String]] = parse(rawJson).toTry
  .flatMap(json => Try(json.asObject.getOrElse(sys.error("Not a JSON Object"))))
  .flatMap(jsonObject => Try(jsonObject.toMap.map{case (name, value) => name -> value.asString.getOrElse(sys.error(s"Field '$name' is not a JSON string"))}))

val map: Map[String, String] = result.get
println(map)

Или с использованием Decoder:

import io.circe.Decoder

val decoder = Decoder.decodeMap(KeyDecoder.decodeKeyString, Decoder.decodeString)

val result = for {
  json <- parse(rawJson).toTry
  map <- decoder.decodeJson(json).toTry
} yield map

val map = result.get
println(map)

Вы можете протестировать следующие недопустимые входные данные и посмотреть, какое исключение будет сгенерировано:

val rawJson: String = """xxx{"a": "x", "b": "y", "c": "z"}""" // invalid JSON
val rawJson: String = """[1,2,3]""" // not a JSON object
val rawJson: String = """{"a": 1, "b": "y", "c": "z"}""" // not all values are string
person ygor    schedule 05.01.2019