Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Сглаживание вложенных объектов JSON с помощью Circe

Автор вопроса: Travis BrownОтветов: 1

Предположим, у меня есть такой объект JSON:

{
   "foo": true,
   "bar": {
      "baz": 1,
      "qux": {
        "msg": "hello world",
        "wow": [null]
      }
   }
}

И я хочу рекурсивно свести его к одному слою, с объединением клавиш с подчеркиванием:

{
   "foo": true,
   "bar_baz": 1,
   "baz_qux_msg": "hello world",
   "baz_qux_wow": [null]
}

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

(Примечание: это еще один часто задаваемый вопрос с канала Цирцеи Гиттер.)

jsonscalacirce

Ответы

Ответ 1 · рейтинг 6 · Travis Brown

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

import io.circe.Json

def flatten(combineKeys: (String, String) => String)(value: Json): Json = {
  def flattenToFields(value: Json): Option[Iterable[(String, Json)]] =
    value.asObject.map(
      _.toIterable.flatMap {
        case (k, v) => flattenToFields(v) match {
          case None => List(k -> v)
          case Some(fields) => fields.map {
            case (innerK, innerV) => combineKeys(k, innerK) -> innerV
          }
        }
      }
    )

  flattenToFields(value).fold(value)(Json.fromFields)
}

Здесь наш внутренний flattenToFields метод принимает каждое значение JSON и либо возвращает None, если это значение объекта, отличное от JSON, в качестве сигнала о том, что это поле не требует сглаживания, либо Some, содержащий последовательность сглаженных полей в случае объекта JSON. .

Если у нас есть такое значение JSON:

val Right(doc) = io.circe.jawn.parse("""{
   "foo": true,
   "bar": {
      "baz": 1,
      "qux": {
        "msg": "hello world",
        "wow": [null]
      }
   }
}""")

Мы можем проверить, что flatten делает то, что мы хотим, вот так:

scala> flatten(_ + "_" + _)(doc)
res1: io.circe.Json =
{
  "foo" : true,
  "bar_baz" : 1,
  "bar_qux_msg" : "hello world",
  "bar_qux_wow" : [
    null
  ]
}

Обратите внимание, что flattenToFields не является хвостовой рекурсией и переполняет стек для глубоко вложенных объектов JSON, но, вероятно, не раньше, чем вы достигнете нескольких тысяч уровней, поэтому на практике это вряд ли будет проблемой. Вы можете сделать его хвостовой рекурсивной без особых проблем, но за счет дополнительных накладных расходов для общих случаев, когда у вас есть только несколько уровней вложенности.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы