2017-06-01 19 views
9

To pytanie pojawiło się kilka razy w ostatnim czasie, więc często zadawałem to pytanie tutaj. Załóżmy, że mam jakieś zajęcia takim wypadku:Moje kodowanie jest niejednoznaczne.

import io.circe._, io.circe.generic.semiauto._ 

object model { 
    case class A(a: String) 
    case class B(a: String, i: Int) 
    case class C(i: Int, b: Boolean) 

    implicit val encodeA: Encoder[A] = deriveEncoder 
    implicit val encodeB: Encoder[B] = deriveEncoder 
    implicit val encodeC: Encoder[C] = deriveEncoder 
    implicit val decodeA: Decoder[A] = deriveDecoder 
    implicit val decodeB: Decoder[B] = deriveDecoder 
    implicit val decodeC: Decoder[C] = deriveDecoder 
} 

I chcę zakodować wartość, która mogłaby być jednym z nich jako JSON za pomocą circe i bezkształtne współproduktów.

import io.circe.shapes._, io.circe.syntax._ 
import shapeless._ 

import model._ 

type ABC = A :+: B :+: C :+: CNil 

val c: ABC = Coproduct[ABC](C(123, false)) 

To wygląda dobrze na początku:

scala> c.asJson 
res0: io.circe.Json = 
{ 
    "i" : 123, 
    "b" : false 
} 

Ale problemem jest to, że nigdy nie można zdekodować koproduktu zawierający element B, ponieważ każdy ważny dokument JSON, które mogą być dekodowane jako B może być również dekodowana jako A, a dekodery współproduktów dostarczone przez circe-forms próbują elementów w kolejności, w jakiej pojawiają się w współproduktach.

scala> val b: ABC = Coproduct[ABC](B("xyz", 123)) 
b: ABC = Inr(Inl(B(xyz,123))) 

scala> val json = b.asJson 
json: io.circe.Json = 
{ 
    "a" : "xyz", 
    "i" : 123 
} 

scala> io.circe.jawn.decode[ABC](json.noSpaces) 
res1: Either[io.circe.Error,ABC] = Right(Inl(A(xyz))) 

Jak mogę ujednoznacznić elementy mojego współproduktu w kodowaniu?

Odpowiedz

9

Moduł circe-shapes koduje zwykłe hlistów i produktów ubocznych bez etykiet, jak widać powyżej: współprodukt jako samodzielna reprezentacja JSON elementu, a hlista jako tablica JSON (taka sama, jak domyślna kodowanie krotek)):

scala> ("xyz" :: List(1, 2, 3) :: false :: HNil).asJson.noSpaces 
res2: String = ["xyz",[1,2,3],false] 

W przypadku hlists nie ma niebezpieczeństwa niejednoznaczności powodu nakładających się nazwami, ale dla współproduktów istnieje. W obu przypadkach można jednak dodawać etykiety do reprezentacji JSON za pomocą mechanizmu etykietowania Shapeless, który oznacza wartości za pomocą symbolu poziomu.

Hlist z etykietami nazywany jest "rekordem", a koproducent z etykietami to "związek". Bezkształtny zapewnia specjalną składnię i operacje dla obu tych i circe-kształtów traktuje oba inaczej od nieoznakowanych hlists lub copcontcts. Na przykład (zakładając, definicje i import powyżej):

scala> import shapeless.union._, shapeless.syntax.singleton._ 
import shapeless.union._ 
import shapeless.syntax.singleton._ 

scala> type ABCL = Union.`'A -> A, 'B -> B, 'C -> C`.T 
defined type alias ABCL 

scala> val bL: ABCL = Coproduct[ABCL]('B ->> B("xyz", 123)) 
bL: ABCL = Inr(Inl(B(xyz,123))) 

scala> val jsonL = bL.asJson 
jsonL: io.circe.Json = 
{ 
    "B" : { 
    "a" : "xyz", 
    "i" : 123 
    } 
} 

scala> io.circe.jawn.decode[ABCL](jsonL.noSpaces) 
res3: Either[io.circe.Error,ABCL] = Right(Inr(Inl(B(xyz,123)))) 

Kodowanie zapisów obejmuje podobnie nazwiska członków:

scala> ('a ->> "xyz" :: 'b ->> List(1) :: 'c ->> false :: HNil).asJson.noSpaces 
res4: String = {"c":false,"b":[1],"a":"xyz"} 

W ogóle, jeśli chcesz hlist i koproduktu kodowania zawierać etykiety (i wyglądają tak, jak kodowania, które otrzymasz z generatora circowego dla klas przypadków i zaplombowanych hierarchii cech), możesz użyć rekordów lub współproduktów, aby przekazać circe-shape, aby to zrobić z minimalną ilością dodatkowego obciążenia składniowego i wykonawczego.

Powiązane problemy