Рассмотрим этот 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
). - Поле даты, проанализированное как
OffsetDateTime
.
По этой причине я создал три класса case, которые моделируют эти комбинации (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)
)
}
Некоторые соображения:
- Основываясь на ответе Трэвиса на этот вопрос, я пытаюсь смоделировать JSON как можно ближе для работы с circe. Вот почему я попробовал модель tuples.
- Основываясь (снова) на ответе Трэвиса на этот вопрос SO, я пытаюсь использовать метод
Decode.prepare(...)
. И вот мой вопрос...
Вопрос: как извлечь конкретное имя ключа текущей позиции курсора и сопоставить его с Tuple
? Мне нужен только текущий ключ, а не весь набор ключей, который возвращает метод .keys
ACursor
. С помощью этого ключа я хочу вручную сопоставить Tuple с текущим именем ключа и извлеченным значением.
Подводя итог, мне нужно преобразовать структуру, которая имеет некоторые неизвестные ключи (имя и положение), извлечь их значения на основе пути, разделенного абсолютными точками, который у меня есть, и поднять как имя ключа, так и имя значения до case class, к которому я добавил суффикс Tuple
.
Можете ли вы пролить свет на это?
Спасибо
downField
. - person James   schedule 23.05.2018