2014-02-22 46 views
1

假設以下兩種JSON代碼段:如何命名JsArray轉換成JsObject與JsZipper

{ "include": ["field1", "field2", "fieldN"] } 
{ "exclude": ["field1", "field2", "fieldN"] } 

我需要轉換include陣列是這樣的...

{ "field1": 1, "field2": 1, "fieldN": 1 } 

...和exclude陣列是這樣的:

{ "field1": 0, "field2": 0, "fieldN": 0 } 

[只爲你的inf ○:在這裏我下面需要輸入JSON轉換爲蒙戈的預測]

是我目前的解決方案–我實現它作爲一個JsValue擴展:

object testTypeExtensions { 

    implicit class TestJsExtensions(val json: JsValue) extends AnyVal { 

    def toProjection: JsValue = { 
     if (json \\ "include" nonEmpty) 
     JsObject(for (field <- (json \ "include").as[List[JsString]]) 
     yield (field.value, JsNumber(1))) 
     else if (json \\ "exclude" nonEmpty) 
     JsObject(for (field <- (json \ "exclude").as[List[JsString]]) 
     yield (field.value, JsNumber(0))) 
     else Json.obj() 
    } 
    } 
} 

上面的代碼工作...

scala> val p = Json.obj("exclude" -> Json.arr("field1", "field2")) 
p: play.api.libs.json.JsObject = {"exclude":["field1","field2"]} 

scala> p.toProjection 
res12: play.api.libs.json.JsObject = {"field1":0,"field2":0} 

...但我相信它可以寫得更好JsZipper

此外,它不是很靈活,因爲它只管理includeexclude鍵,而我想還可以管理其他類似的情況下,像選對象:

{ "asc": ["field1", "field2"] } 
{ "desc": ["field1", "field2"] } 

...轉化成......

{ "field1": 1, "field2": 1 } 

...和

{ "field1": -1, "field2": -1 } 

這就是說,我所考慮的是經營任何一種名爲JSON陣列的像普通的方法:

object testypeExtensions { 

    implicit class TempJsExtensions(val json: JsValue) extends AnyVal { 

    def namedArrayToObject(keys: String*): JsValue = { 
     // how to implement it, possibly with JsZipper 
    } 

    } 
} 

namedArrayToObject方法應該在當前的JSON搜索指定keys,併產生了一個對象第一場比賽像我在本文開頭描述的那些,可能與JsZipper。這是對預期結果的模擬。

搜索excludeinclude並返回第一個匹配的JsObject

scala> val p = Json.obj("exclude" -> Json.arr("field1", "field2")) 
scala> p.namedArrayToObject("exclude", "include") 
res12: play.api.libs.json.JsObject = {"field1":0,"field2":0} 

和以前一樣......但現在輸入JSON包含include而不是exclude

scala> val p = Json.obj("include" -> Json.arr("field1", "field2")) 
scala> p.namedArrayToObject("exclude", "include") 
res12: play.api.libs.json.JsObject = {"field1":1,"field2":1} 

搜索ascdesc並將第一個匹配返回爲JsObject

scala> val p = Json.obj("desc" -> Json.arr("field1", "field2")) 
scala> p.namedArrayToObject("asc", "desc") 
res12: play.api.libs.json.JsObject = {"field1":-1,"field2":-1} 

...等等。

如果沒有匹配,namedArrayToObject應該返回一個空的JsObject。任何關於如何以正確的方式實施這一建議將非常感謝。

回答

0

特拉維斯幫我找到路...這裏最終以下是我的解決方案,它正是我一直在尋找:

object testExtensions { 

    implicit class testJsExtensions(val json: JsValue) extends AnyVal { 

    def toParams(pairs: (String, Int)*): JsValue = { 
     for (pair <- pairs) { json.getOpt(__ \ pair._1).map { values => 
     JsObject(for (field <- values.as[List[JsString]]) 
     yield (field.value, JsNumber(pair._2))) 
     } match { 
     case Some(params) => return params 
     case _ => 
     }} 
     Json.obj() 
    } 
    } 
} 

scala> example.toParams(("include", 1), ("exclude", 0)) 
res63: play.api.libs.json.JsValue = {"field1":1,"field2":1,"field3":1} 

scala> example.toParams(("exclude", 0)) 
res64: play.api.libs.json.JsValue = {"field4":0,"field5":0,"field6":0} 

又有點感謝特拉維斯:-)

1

你可以用JSON transformations做到這一點非常直截了當:

import play.api.libs.json._ 

def toObj(value: Int) = Reads.of[List[String]].map(
    keys => Json.toJson(keys.map(_ -> value).toMap) 
) 

val transformation = 
    (__ \ 'include).json.update(toObj(1)) andThen 
    (__ \ 'exclude).json.update(toObj(0)) 

我們可以定義一個例子中的物體,並應用我們的轉型:

​​

然後:

scala> transformed.foreach(Json.prettyPrint _ andThen println) 
{ 
    "include" : { 
    "field1" : 1, 
    "field2" : 1, 
    "field3" : 1 
    }, 
    "exclude" : { 
    "field4" : 0, 
    "field5" : 0, 
    "field6" : 0 
    } 
} 

這並未完全符合你想要的API,但它應該很容易適應,而且我建議遠離隱式類業務,無論如何,它不太可組合,並且使處理無效輸入變得不那麼優雅。

+0

的Tx爲你的解釋。問題是輸入JSON不能同時包含「include」和「exclude」;如果是這樣,只應該進行第一場比賽。例如,將「示例」JSON作爲輸入,「toObject」應該返回{「field1」:1,「field2」:1,「field3」:3}。這就是爲什麼我正在考慮JsZipper ... – j3d

+0

通過組合'orElse','和Then',和'prune',你可以得到接近這種行爲的東西,但是JSON對象是無序的,所以「_first_match」不會至少不應該)意味着什麼。 –