2016-03-24 6 views
0

In Scala mit Play und Slick habe ich enum Country als Case-Klasse implementiert und möchte es in JSON als String lesen/schreiben. Dies ist der vollständige Code:Wie liest man String als Enum-Klasse in Play Scala?

Das Land enum wird von anderen Modellen verwendet, z.

{ 
    "name": "Robo" 
    "country": "DE" 
} 

Der obige Code schreibt die Enum als einfachen String das ist, was ich will, aber ich kann nicht herausfinden, wie der impliziten Leseteil zu implementieren. Derzeit würden die Lese nur funktionieren, wenn die JSON wie ist:

{ 
    "name": "Robo" 
    "country": { 
    "id": "DE" 
    } 
} 

Ich bin sicher, dass die Antwort einfach ist, kann einfach nicht die richtige Syntax herauszufinden.

+0

Ihre JSONs sind Kommas zwischen Felddeklarationen fehlen. –

Antwort

1
import play.api.libs.json._ 
import play.api.libs.json.Reads._ 

implicit val reads = new Reads[Country] { 
    override def reads(json: JsValue): JsResult[Country] = for { 
    id <- (json \ "country").validate[String] 
    } yield Country(id) 
} 

(json \ "country").validate[String] liefert eine JsResult[String] die dann abgebildet wird (via yield) zu einem JsResult[Country]

oder

import play.api.libs.json._ 
import play.api.libs.json.Reads._ 

implicit val reads = new Reads[Country] { 
    override def reads(json: JsValue): JsResult[Country] = 
    (json \ "country").validate[String].map(Country(_)) 

oder

import play.api.libs.json._ 
import play.api.libs.json.Reads._ 
import play.api.libs.functional.syntax._ 

implicit val reads: Reads[Country] = (__ \ "country").reads[String].map(Country(_)) 

EDIT:

In Bezug auf Ihren Kommentar zu Validierung, würde ich das auf Ihren Fall Klasse fügen Sie einfach, nicht die json-Parser: ist

case class Country(id: String) extends MappedTo[String] { 
    private val validIds = Set(France, Germany).map(_.id) 

    require(validIds contains id, s"id must be one of $validIds") 

    override def value: String = id 
} 

der Trick, dass Anweisungen, die Sie in den Körper gelegt von Ihre Klasse wird Teil des Konstruktors sein. Der Aufruf wird eine Ausnahme auslösen, wenn jemand versucht, eine Country mit einer ID zu erstellen, die nicht in Ihrer Liste der zulässigen IDs ist.

+0

Vielen Dank, fast da. Gibt es eine Möglichkeit sicherzustellen, dass der Wert nur eine der definierten "enum" ist, d. H. DE oder FR? Derzeit, wenn die Zeichenfolge UK ist, erlaubt der Code es. – Robo

+0

@Robo Ich habe eine Lösung dafür hinzugefügt. –

0

Ich empfehle die Verwendung von versiegelten Klasse. Wenn der Selektor einer Musterübereinstimmung eine Instanz einer versiegelten Klasse ist, kann die Kompilierung der Musterübereinstimmung Warnungen ausgeben, die diagnostizieren, dass eine gegebene Menge von Mustern nicht erschöpfend ist, dh dass die Möglichkeit besteht, dass zur Laufzeit ein MatchError ausgelöst wird .

Beispiel aus meinem Projekt:

import com.pellucid.sealerate 
import play.api.libs.json._ 

sealed abstract class TokenKind(val value: String) 

object TokenKind { 

    case object PasswordReset extends TokenKind("passwordReset") 
    case object AccountConfirmation extends TokenKind("accountConfirmation") 

    def values: Set[TokenKind] = sealerate.values[TokenKind] 

    def apply(value: String): TokenKind = values.find(_.value == value).getOrElse(throw new RuntimeException(s"Can't construct TokenKind from: $value")) 

    implicit def tokenKindWrites = new Writes[TokenKind] { 
    override def writes(o: TokenKind): JsValue = JsString(o.value) 
    } 

    implicit def tokenKindReads = new Reads[TokenKind] { 
    override def reads(json: JsValue): JsResult[TokenKind] = json match { 
     case JsString(string) => JsSuccess(TokenKind(string)) 
     case _ => JsError("validate.error.invalidTokenKind") 
    } 
    } 

} 
0

Ich hatte ein ähnliches Problem, das auf diese Weise gelöst:

object VoteType extends Enumeration { 
    val Up, Down = Value 

    implicit val voteTypeMappeer = MappedColumnType.base[VoteType.Value, String](_.toString, VoteType.withName) 
}