Изменение объекта JSON с помощью lift-json

Я нахожусь в процессе выбора хорошей библиотеки Scala JSON, и кажется, что консенсус lift-json в настоящее время лучший выбор.

После игры с ним (версия 2.5.1) для заклинания я смог довольно легко сделать большинство вещей, которые мне были нужны, за исключением одного: изменение существующего JValue,

Скажи, что у меня есть следующий экземпляр JValue:

val john = ("id"   -> 1) ~
           ("name" -> "Foo") ~
           ("nested" ->
             ("id" -> 2) ~
             ("name" -> "Bar"))

Я хотел бы изменить имя родительского элемента с Foo в foo, Я думал transform метод был способ пойти:

john transform {
    case JField("name", _) => JField("name", "foo")
})

Но это меняет как родительский, так и вложенный элемент name поля для foo - что, оглядываясь назад, действительно не должно было быть сюрпризом.

Я просмотрел документацию и код и не смог найти способ выбрать конкретное поле с ключом name, Я что-то пропустил?

Другое решение (и это работает), кажется, объединяет два JValue объекты, но это кажется немного многословным:

john merge JObject(JField("name", "foo") :: Nil)

Есть ли встроенный, более читаемый способ достижения того же результата? Я, вероятно, мог бы написать неявное преобразование из JField в JObject, но кажется странным, что lift-json еще нет такого механизма. Если бы мне пришлось делать ставку, это было бы от того, что я ее не нашел, а не от того, что она не существует.

РЕДАКТИРОВАТЬ: я чувствую себя немного глупо сейчас

john transform {
    case JField("name", "Foo") => JField("name", "foo")
})

Не самое оптимальное решение в мире, но отлично читаемое и лаконичное.

2 ответа

Использовать replace метод JValue, как это:

john.replace(List("name"), "foo")

Реализация этого метода "замены" - это катастрофа, и имя "замена" предлагает (для меня) изменение состояния, которое не соответствует действительности, но оно делает то, что нужно

JObject(List((id,JInt(1)), (name,JString(foo)), (nested,JObject(List((id,JInt(2)), (name,JString(Bar)))))))

Это уродливо, так как в нем используется изменяемая переменная, а также json4s (который использует lift-json под капотом), но он работает:

import org.json4s.JsonAST._
import org.json4s.native.JsonMethods._
import org.json4s.JsonDSL._

object JsonTesting {
  def main(args: Array[String]) {
    val john = ("id"   -> 1) ~
           ("name" -> "Foo") ~
           ("nested" ->
             ("id" -> 2) ~
             ("name" -> "Bar"))

    var changed = false
    val john2 = john transformField {
      case JField("name", _) if !changed => 
        changed = true
        JField("name", "foo")
    }
  }
}

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

Другие вопросы по тегам