2014-02-15 43 views
2

在將任何JSON文檔存儲在MongoDB中之前,我需要將文檔中的所有字符串id s轉換爲BSON id s,反之亦然,在讀取任何來自MongoDB的文檔,我需要將所有BSON id s轉換爲字符串id。也就是說,鑑於以下JSON ...如何用JSON樹中的相同鍵替換所有值

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

...我需要把它轉化爲將其存儲在MongoDB中之前,這裏介紹以下...

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

...當然當從MongoDB讀取文檔時,我需要將所有BSON id s轉換爲字符串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的第二個例子。我有點失落,所以任何幫助將非常感激。

回答

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) 
} 

你或許可以摘要公平一點更很好地處理你的特殊情況:「已用/從方式發出。例如,你可以申請將其與乘/從關鍵規則的地圖:

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":"[email protected]","username":"joe","subscriptions":[{"accountId":{"$oid":"72fe942b790000790079b755"},"name":"test 1","isDefault":true},{"accountId":{"$oid":"72fe942b796850790079b743"},"name":"test 2","isDefault":false}]} 

注:一個原因,你的第一個例子不工作就是你試圖模式匹配key值,但它是相反創建一個新的陰影變量綁定,稱爲key(我經常在斯卡拉做錯了。)相反,你需要使用模式匹配後衛,如case ((__ \ path), _) if path == key => ...

+0

令人驚歎!非常感謝您......您讓我擺脫了麻煩:-)只是最後一個問題:如果我將內部JSON傳遞給fromPublic,則不會發生轉換,並且會獲得原始JSON。這是正確的。但是,如果我將公共JSON傳遞給toPublic,則會將accountId設置爲null的轉換JSON。如果toPublic獲取一個公共JSON作爲輸入,它應該返回輸入JSON而不像FromPublic那樣進行轉換。有沒有解決方法? – j3d

+0

@ j3d:我很想說「不要那樣做」;)。但是你可以用這樣的東西來處理它,可能是:https://gist.github。com/mikesname/9027468 – Mikesname

+0

非常感謝您的大力支持! – j3d

相關問題