Конвертировать Json в карту [String, String] - PullRequest
0 голосов
/ 05 января 2019

У меня есть ввод 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]])

Ответы [ 2 ]

0 голосов
/ 05 января 2019

Хотя решения из другого ответа работают, они гораздо более многословны, чем необходимо. Готовый 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 или что-то еще, это почти конечно, лучше сделать это в конце).

0 голосов
/ 05 января 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
...