Scala将JSON转换为多种类型之一

时间:2016-10-19 19:07:10

标签: json scala

我从流中收到一些JSON。

该对象可以由许多scala类型之一表示。例如:{"userid":"blerk","name":"Fred"}将为User(userId:String, userName:String)

{"groupId":"zerk","name":"Accounting"}将为Group(groupId:String,groupName:String}

(现实生活中的对象有更多元素)。

如果重要的话,我正在使用spray-json,但我想知道的是:

我希望有一个“转换”功能,按顺序执行Try(myJson.convertTo[T])直到成功,然后返回T类型的值。

我的目标是避免使用某种字符串匹配启发式方法提前弄清楚它可能是什么类型,并且只能在新案例出现时将新类型附加到列表中。我还想避免像

这样的代码
val t1=Try(myJson.convertTo[User])
if(t1.isSuccess) return t1.get
val t2=Try(myJson.convertTo[Group])
if(t2.isSuccess) return t2.get

可悲的是,我在缠绕这个小脑子时遇到了麻烦,并且感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

您可以使用Try' orElse method来执行您要求的操作。这是第一次尝试:

import scala.util.Try
import spray.json._, DefaultJsonProtocol._

case class User(userId: String, userName: String)
case class Group(groupId: String, groupName: String)

implicit val userFormat: JsonFormat[User] = jsonFormat2(User)
implicit val groupFormat: JsonFormat[Group] = jsonFormat2(Group)

val groupJson = """{ "groupId": "zerk", "groupName": "Accounting" }""".parseJson
val userJson = """{ "userId": "foo", "userName": "Foo McBar" }""".parseJson

val result = Try(groupJson.convertTo[User]).orElse(Try(groupJson.convertTo[Group]))

如果要尝试的类型超过两种,则可以将尝试按顺序排列,并使用_ orElse _折叠它们。

这会有效,但是它会为你提供Try[Any]类型的结果,如果你关心类型安全,这几乎没用,尽管如果你愿意,你总是可以对结果进行模式匹配处理原则上你可以在那里做任何事情。

更好的方法是解码为Either

scala> groupJson.convertTo[Either[Group, User]]
res3: Either[Group,User] = Left(Group(zerk,Accounting))

scala> userJson.convertTo[Either[Group, User]]
res4: Either[Group,User] = Right(User(foo,Foo McBar))

这也可以推广到两种以上类型,尽管你必须嵌套Either s:

scala> userJson.convertTo[Either[Either[Group, String], User]]
res5: Either[Either[Group,String],User] = Right(User(foo,Foo McBar))

如果您想要一种更清晰的方式来处理可以最终成为两种类型之一的结果,Shapeless' Coproduct是一个很好的原则解决方案。您需要为副产品编写自己的格式,但这是一个相对简单的练习。

然而,这是极端类型安全路线。如果您不在乎,请使用orElse