2017-03-15 11 views
1

私は、次のJSONを生成することができScalaでは基本特性/クラスを設計したいと思います:どのように動的ベースjsonオブジェクトを定義できますか?

trait GenericResource { 
    val singularName: String 
    val pluralName: String 
} 

私はケースクラスの中で、この形質を継承する:

case class Product(name: String) extends GenericResource { 
    override val singularName = "product" 
    override val pluralName = "products" 
} 
val car = Product("car") 
val jsonString = serialize(car) 

出力のようになります。 :{"product":{"name":"car"}}

Seq[Product]{"products":[{"name":"car"},{"name":"truck"}]}などを生産する必要があります...

私はこれを達成するために適切な抽象化に苦労しています。私はすべてのJSONライブラリ(Scalaで利用可能)を使用してソリューションを公開しています。

答えて

2

ここで私はcirceで一般的に特異一部を行うと考えることができます最も簡単な方法についてです:あなたはこのようなGenericResourceを拡張し、いくつかのケースクラスを持っているならば、

import io.circe.{ Decoder, Encoder, Json } 
import io.circe.generic.encoding.DerivedObjectEncoder 

trait GenericResource { 
    val singularName: String 
    val pluralName: String 
} 

object GenericResource { 
    implicit def encodeResource[A <: GenericResource](implicit 
    derived: DerivedObjectEncoder[A] 
): Encoder[A] = Encoder.instance { a => 
    Json.obj(a.singularName -> derived(a)) 
    } 
} 

そして:

case class Product(name: String) extends GenericResource { 
    val singularName = "product" 
    val pluralName = "products" 
} 

あなたは(ケースクラスのすべてのメンバーがエンコード可能であると仮定して)これを行うことができます:

などはありません定型、余分な輸入、

キルケが自動的Encoderを持つ任意のAためSeq[A]エンコーダを提供するので、Seq場合は、少し複雑ですが、それはあなたが欲しい、それが何をしていないだけでアイテムをエンコードし、JSON配列に貼り付けます。あなたはこのような何か書くことができます。

implicit def encodeResources[A <: GenericResource](implicit 
    derived: DerivedObjectEncoder[A] 
): Encoder[Seq[A]] = Encoder.instance { 
    case values @ (head +: _) => 
    Json.obj(head.pluralName -> Encoder.encodeList(derived)(values.toList)) 
    case Nil => Json.obj() 
} 

をそして、このようにそれを使用します。

scala> Seq(Product("car"), Product("truck")).asJson.noSpaces 
res1: String = {"products":[{"name":"car"},{"name":"truck"}]} 

しかし、あなただけのコンパニオンオブジェクトでそれを固執し、すべてが動作し、あなたがして、それを配置する必要が期待することはできません必要なときにインポートします(そうでない場合は、デフォルトのSeq[A]インスタンスと同じ優先度が設定されます)。

このencodeResources実装の別の問題は、Seqが空の場合、それはちょうど空のオブジェクトを返すことです。

scala> Seq.empty[Product].asJson.noSpaces 
res2: String = {} 

複数の名前は、インスタンス・レベルでリソースに接続されているので、これは、あなたの場合インスタンスを持っていないので、それを取得する方法はありません(リフレクションの不足)。もちろん、nullをコンストラクタなどに渡すことで、偽のインスタンスを作成することはできますが、それはこの質問の範囲外です。

エンコードしたこのJSONをデコードする必要がある場合は、この問題(インスタンスにアタッチされるリソース名)も問題になります。そのような場合は、特定のリソースタイプのコンパニオンオブジェクトに混在させ、そこに名前を示すような形のものがある場合は、少し異なるアプローチを検討することをお勧めします。GenericResourceCompanionそれがオプションでない場合は、おそらく反射や偽のインスタンス、またはその両方に悩まされているでしょう(しかし、おそらくこの質問の対象外です)。

+0

ワウ、詳細なソリューションに感謝します。 –

関連する問題