2017-02-10 124 views
1

我有一些JSON來自我無法控制的外部API。在JSON的一部分被劃成這樣:如何將JSON字段轉換爲Scala Play框架2中的Seq?

{ 
    "room_0": { 
    "area_sq_ft": 151.2 
    }, 
    "room_1": { 
    "area_sq_ft": 200.0 
    } 
} 

而不是使用數組像他們應該擁有的,他們使用room_n一個關鍵要素的n個。不再創建room_0,room_1,room_2等的情況下類,我想將其轉換爲一個序列[房間]其中,這是我的房間的情況下類:

case class Room(area: Double) 

我使用讀取play.api.libs.json轉換JSON的其他部分轉換爲大小寫類,並且傾向於使用Reads進行此轉換。我怎麼能做到這一點?

這是我試過的。

val sqFtReads = (__ \ "size_sq_ft").read[Double] 
val roomReads = (__ \ "size_sq_ft").read[Seq[Room]](sqFtReads).map(Room) 
cmd19.sc:1: overloaded method value read with alternatives: 
    (t: Seq[$sess.cmd17.Room])play.api.libs.json.Reads[Seq[$sess.cmd17.Room]] <and> 
    (implicit r: play.api.libs.json.Reads[Seq[$sess.cmd17.Room]])play.api.libs.json.Reads[Seq[$sess.cmd17.Room]] 
cannot be applied to (play.api.libs.json.Reads[Double]) 
val roomReads = (__ \ "size_sq_ft").read[Seq[Room]](sqFtReads).map(Room) 
+0

您是否閱讀過[documentation](https://www.playframework.com/documentation/2.5.x/ScalaJson)?你有什麼嘗試? – cchantep

+0

@cchantep是的,我已經過了幾次文檔,但我沒有看到任何幫助我處理這個用例的東西。 –

+0

首先,JSON示例不是一個數組(對應於Scala集合,例如'Seq'),而是一個帶有「room_0」和「room_1」鍵的詞典。那麼你最好看看Play JSON宏來定義一個'Reads [Room]'。 – cchantep

回答

1

一個棘手的小挑戰,但完全可以用Reads實現。

首先,Reads[Room] - 即轉換器,用於單個Room實例:

val roomReads = new Reads[Room] { 
    override def reads(json: JsValue): JsResult[Room] = { 
    (json \ "area_sq_ft").validate[Double].map(Room(_)) 
    } 
} 

非常簡單;我們瀏覽JSON並嘗試找到稱爲area_sq_ft的頂級字段,該字段驗證爲Double。如果一切正常,我們會根據需要返回填充的實例Room

接下來,轉換器爲您的上游對象,在良好的Postel's Law時尚,你正在爲自己的消費者清理。

val strangeObjectReads = new Reads[Seq[Room]] { 
    override def reads(json: JsValue): JsResult[Seq[Room]] = { 

    json.validate[JsObject].map { jso => 

     val roomsSortedNumerically = jso.fields.sortBy { case (name, contents) => 
     val numericPartOfRoomName = name.dropWhile(!_.isDigit) 
     numericPartOfRoomName.toInt 
     } 

     roomsSortedNumerically.map { case (name, contents) => 
     contents.as[Room](roomReads) 
     } 

    } 
    } 
} 

這裏關鍵的是json.validate[JsObject]圍繞整個地段。通過map ping我們得到JsResult,我們需要包裝整個事情,另外,我們可以訪問JSON對象內的fields,它定義爲Seq[(String, JsValue)]

爲確保我們在輸出序列中按照正確的順序放置字段,我們對字符串進行了一些處理,得到了字符串的數字部分,並將其用作sortBy條件。我在這裏有點幼稚,假設你的上游服務器不會做任何討厭的事情,比如跳過房間號碼!

將房間按照數字順序排序後,我們只需要map就可以了,用我們的roomReads轉換器轉換每個房間。

您可能已經注意到我的自定義Reads實現絕對是而不是單行程。這來自古怪的上游JSON格式的處理經驗。稍微詳細一點,當上遊服務器突然改變其JSON格式時,使用更多的變量並將事情分解一點就能帶來巨大的回報!

+0

完美!謝謝@millhouse! –

相關問題