2016-10-19 3 views
1

Ich erhalte einige JSON aus einem Stream.Scala konvertieren JSON zu einer aus einer Reihe von Typen

Das Objekt könnte durch einen von mehreren Skalatypen dargestellt werden. Zum Beispiel: {"userid":"blerk","name":"Fred"} wäre User(userId:String, userName:String)

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

(realen Objekte haben viele weitere Elemente) sein würde.

Ich bin mit Spray-json wenn es ankommt, aber was ich wissen will, ist dies:

Ich mag würde, eine Funktion haben „konvertieren“, dass die Reihe nach hat ein Try(myJson.convertTo[T]), bis es erfolgreich ist, und gibt einen Wert vom Typ T zurück.

Mein Ziel ist es, zu vermeiden, eine Art von String-Matching-Heuristiken zu verwenden, um im Voraus herauszufinden, welchen Typ es sein kann, und einfach einen neuen Typ an eine Liste anhängen, wenn ein neuer Fall auftaucht. Ich möchte auch leider Code wie

val t1=Try(myJson.convertTo[User]) 
if(t1.isSuccess) return t1.get 
val t2=Try(myJson.convertTo[Group]) 
if(t2.isSuccess) return t2.get 

vermeiden, ich habe Probleme mein kleines Gehirn, um dieses Einwickeln und wäre für jede Hilfe dankbar.

Antwort

0

Sie können Try 's orElse method verwenden, um zu tun, was Sie verlangen. Hier ist ein erster Versuch:

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

Wenn Sie mehr als zwei Typen haben, um zu versuchen, können Sie die Versuche in einer Reihenfolge gebracht und falten sie mit _ orElse _.

Dies funktioniert, aber es gibt Ihnen ein Ergebnis des Typs Try[Any], die ziemlich nutzlos ist, wenn Sie sich um Typ Sicherheit kümmern, obwohl Sie Muster immer auf das Ergebnis passen, wenn Sie bereit sind, mit der Tatsache umzugehen im Prinzip könnte man dort alles haben.

Ein besserer Ansatz ist in eine Either zu entschlüsseln:

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

Dies galt auch für mehr als zwei Typen verallgemeinert, obwohl Sie nisten haben Either s:

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

Wenn Sie einen Reiniger wollen Arbeitsweise mit einem Ergebnis, das als einer von mehr als zwei Arten enden kann, Coproduct ist eine nette prinzipielle Lösung. Sie müssen Ihr eigenes Format für Coprodukte schreiben, aber es ist eine relativ einfache Übung.

Das geht jedoch die extreme Art-Sicherheitsroute. Wenn Sie nicht interessiert sind, verwenden Sie einfach orElse.

+0

Danke; Da es sich um eine Lernübung handelt, bin ich nicht bereit, auf Typ-Sicherheit (zumindest noch nicht) zu verzichten. Und da es einige mögliche Typen gibt, ist auch die verschachtelte "Entweder" -Route nicht ganz zufriedenstellend ;-). Ich schätze die Verbindung zu Shapeless, da ihr 'ShapelessStream' aussieht, als könnte es auch nützlich sein! – Mike

Verwandte Themen