2017-02-10 89 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! –

相关问题