«Динамическое» JSON-декодирование с помощью Circe - PullRequest
0 голосов
/ 23 мая 2018

Рассмотрим этот JSON:

{
  "myDocument": {
    "static_key": "value",
    "dynamic_key": "value",
    "static_key2": "value2",
    "dynamic_key2": {
      "dynamic_key3": "value3"
    }
  }
}

Документы JSON, которые я собираюсь обработать, имеют некоторые статические ключи (поля, которые, как я знаю, будут присутствовать всегда), но имеют некоторые другие, которые могут бытьили не присутствовать, с заранее неизвестными именами для сопоставления их некоторым case class.

У меня есть абсолютный путь к полю в строке (извлекается из конфигурации, хранящейся в базе данных) со следующей структурой:

"myDocument.dynamic_key2.dynamic_key3"

Я знаю, что мне нужно иметь ADT для отображения туда и обратно, поэтому я пришел к следующему:

sealed trait Data

final case class StringTuple(key: String, value: String) extends Data

object StringTuple {

  implicit val encoder: Encoder[StringTuple] = deriveEncoder[StringTuple]
  implicit val decoder: Decoder[StringTuple] = deriveDecoder[StringTuple]
}

final case class NumericTuple(key: String, value: Double) extends Data

object NumericTuple {

  implicit val encoder: Encoder[NumericTuple] = deriveEncoder[NumericTuple]
  implicit val decoder: Decoder[NumericTuple] = deriveDecoder[NumericTuple]
}

final case class DateTuple(key: String, value: OffsetDateTime) extends Data

object DateTuple {

  implicit val encoder: Encoder[DateTuple] = deriveEncoder[DateTuple]
  implicit val decoder: Decoder[DateTuple] = deriveDecoder[DateTuple]
}

final case class TransformedJson(data: Data*)

object TransformedJson {

  def apply(data: Data*): TransformedJson = new TransformedJson(data: _*)

  implicit val encoder: Encoder[TransformedJson] = deriveEncoder[TransformedJson]
  implicit val decoder: Decoder[TransformedJson] = deriveDecoder[TransformedJson]
}

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

  • Числовое поле, что я собираюсь проанализировать как Double.
  • Поле String проанализировано как есть (String).
  • Поле даты проанализировано как OffsetDateTime.

По этой причине я создал три класса дел, которые моделируют этиКомбинации (NumericTuple, StringTuple и DateTuple), и моя идея состоит в том, чтобы создать вывод JSON, подобный этому:

{
  "dynamic_key": "extractedValue",
  "dynamic_key3": "extractedValue3",
  ...
}

("Обычный", без вложенности вообще).

Моя идея состоит в том, чтобы создать список Data объектов для достижения этой цели, и у меня есть что-то вроде этого:

def extractValue(confElement: Conf, json: String) = {
    val cursor: HCursor = parse(json).getOrElse(Json.Null).hcursor
    val decodeDynamicParam = Decoder[NumericTuple].prepare(
      /*
          Here I think (not sure) that I can extract the value with the decoder,
          but, how can I extract the key name and set it, alongside with the extracted
          value?
       */
      _.downField(confElement.path)
    )
  }

Некоторые соображения:

  1. На основании ответа Трэвиса на этот вопрос Я пытаюсь смоделировать JSON как можно ближе для работы с Circe.Вот почему я пытался использовать модель tuples .
  2. Основываясь (опять же) на ответе Тревиса на этот вопрос SO , я пытаюсь использовать метод Decode.prepare(...).И вот возникает мой вопрос ...

Вопрос: Как мне извлечь конкретное имя клавиши текущей позиции курсора и сопоставить его с Tuple?Мне нужен только текущий ключ, а не весь набор ключей, который возвращает .keys метод ACursor.С помощью этого ключа я хочу вручную отобразить кортеж с текущим именем ключа и извлеченным значением.

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

Можете ли вы пролить свет на это??

Спасибо

...