如何使用JSON树中的相同键替换所有值

时间:2014-02-15 17:31:14

标签: json scala playframework

在MongoDB中存储任何JSON文档之前,我需要将文档中的所有字符串id转换为BSON id s,反之亦然,当我从MongoDB读取任何文档时,我需要将所有BSON id转换为字符串id。也就是说,给出以下JSON ......

{
  "id" : "52fe942b790000790079b7d0",
  "email" : "joe@domain.com",
  "username" : "joe",
  "subscriptions" : [
    {
      "accountId" : "72fe942b790000790079b755",
      "name" : "test 1",
      "isDefault" : true
    },
    {
      "accountId" : "72fe942b796850790079b743",
      "name" : "test 2",
      "isDefault" : false
    }
  ]
}

...我需要在将其存储在MongoDB中之前将其转换为如下所述...

{
  "_id" : {"$oid" : "52fe942b790000790079b7d0"},
  "email" : "joe@domain.com",
  "username" : "joe",
  "subscriptions" : [
    {
      "accountId" : {"$oid" : "72fe942b790000790079b755"},
      "name" : "test 1",
      "isDefault" : true
    },
    {
      "accountId" : {"$oid" : "72fe942b796850790079b743"},
      "name" : "test 2",
      "isDefault" : false
    }
  ]
}

...当然,我需要在从MongoDB读取文档时将所有BSON id转换回字符串id

以下是我尝试将BSON id转换为字符串id的代码(使用JsZipper库):

def toPublic(json: JsValue, key: String) = json.updateAllKeyNodes {
  case ((__ \ key), value) => (key -> value \ "$oid")
}

鉴于此方法未将id转换为_id,它根本不起作用并始终返回res0: play.api.libs.json.JsValue = {"accounts":null};另一方面,如果我像这样对密钥进行硬编码......

def toPublic(json: JsValue) = json.updateAllKeyNodes {
  case ((__ \ "accountId"), value) => ("accountId" -> value \ "$oid")
}

...它按预期工作,我在第二个例子中找回了JSON。我有点失落,所以任何帮助都会非常感激。

1 个答案:

答案 0 :(得分:4)

此答案假设您按play-json-zipper使用this question

鉴于您的数据中存在不一致(id - > _id等),我认为没有一些硬编码就不会轻易解决这个问题:

这是一个开始,它处理你以一种方式给出的情况:

def toPublic(json: JsValue) = json.updateAllKeyNodes {
  case ((_ \ "_id"), value) => "id" -> value \ "$oid"
  case ((_ \ "accountId"), value) => "accountId" -> value \ "$oid"
}

def fromPublic(json: JsValue) = json.updateAllKeyNodes {
  case ((_ \ "id"), JsString(value)) => "_id" -> Json.obj("$oid" -> value)
  case ((_ \ "accountId"), JsString(value)) => "accountId" -> Json.obj("$oid" -> value)
}

你可以抽象一点,以更好地处理你的特殊情况。例如,您可以使用to / from键规则将其与地图相乘:

def fromPublicWithKeys(json: JsValue, keys: Map[String,String]): JsValue = {
  def fromPublic(json: JsValue, keys: (String,String)) = json.updateAllKeyNodes {
    case ((_ \ key), JsString(value)) if key == keys._1 => keys._2 -> Json.obj("$oid" -> value)
  }
  keys.foldLeft(json)(fromPublic)
}

用法:

fromPublicWithKeys(stdJson, Map("id" -> "_id", "accountId" -> "accountId"))
// play.api.libs.json.JsValue = {"_id":{"$oid":"52fe942b790000790079b7d0"},"email":"joe@domain.com","username":"joe","subscriptions":[{"accountId":{"$oid":"72fe942b790000790079b755"},"name":"test 1","isDefault":true},{"accountId":{"$oid":"72fe942b796850790079b743"},"name":"test 2","isDefault":false}]}

注意:您的第一个示例不起作用的一个原因是您尝试模式匹配key值,而是创建一个名为key的新阴影变量绑定(我<强大>经常在Scala中做错了。)相反,您需要使用模式匹配 guard ,例如case ((__ \ path), _) if path == key => ...