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