Сглаживание вложенных объектов JSON с помощью Circe - PullRequest
3 голосов
/ 20 сентября 2019

Предположим, у меня есть объект 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 ?

(Примечание: это еще один FAQ из канала Circe Gitter .)

1 Ответ

4 голосов
/ 20 сентября 2019

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

...