2017-06-01 21 views
9

Bu soru birkaç kez gündeme geldi, bu yüzden burada SSS yapıyorum. Böyle bir durumda sınıfları var varsayalım:Coproduct kodlamasım belirsiz

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 
} 

Ve circe ve Şekilsiz Koçarpımlar kullanarak JSON olarak bunlardan herhangi birini olabilecek bir değer kodlamak istiyorum.

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

Ama sorun B olarak deşifre edilebilir herhangi bir geçerli JSON belge aynı zamanda olabilir çünkü, bir B eleman içeren bir eşçarpımı deşifre asla olmasıdır:

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

Bu ilk iyi görünüyor A olarak kodu çözülür, ve circe-şekilleri tarafından temin edilen yardımcı kod çözücüler, elemanları koproductda göründükleri sırayla deneyler.

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

nasıl kodlamasında benim birlikte-unsurlarını sorun aşılabilir?

cevap

9

Circe-shaped modülü, yukarıda görüldüğü gibi, etiketsiz sıradan hlist ve coproduct'leri kodlar: öğenin çıplak JSON gösterimi olarak bir yan ürün ve bir hlist sadece bir JSON dizisi olarak biter (varsayılan tuple kodlamasıyla aynıdır)):

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

hlists durumunda çünkü örtüşen isimlerin belirsizlik tehlikesi yoktur, ancak birlikte-ürünleri için vardır. Her iki durumda da, Shapeless'in etiketleme mekanizmasını kullanarak, değer düzeyindeki bir sembolle değerleri etiketleyen JSON temsiline etiketler ekleyebilirsiniz.

Etiketli bir hlisteye "kayıt" denir ve etiketli bir yan ürün "birleştirme" dir. Shapeless, her ikisi için de özel sözdizimi ve operasyonlar sağlar ve sirküler şekiller, etiketlenmemiş hlistlerden veya yan ürünlerden farklı olarak davranır.

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

kayıtları için kodlama benzer üyesi adlarını içerir: Örneğin: (yukarıdaki tanımları ve ithalat varsayarak) Genelde

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

etiketleri içerecek şekilde hList ve birlikte-kodlamaları istiyorsanız (ve vaka sınıfları ve mühürlenmiş özellik hiyerarşileri için circe-jenerikden alacağınız kodlamalara benzeyebilirsiniz), çok az miktarda ek sözdizimsel ve çalışma zamanı yükü ile bunu yapmak için circe-şekillerine söylemek için kayıtları veya yardımcı ürünleri kullanabilirsiniz.