2016-11-22 5 views
3

DecodeJson[T]を書き込もうとしているJSONオブジェクトには、さまざまな「型」の配列が含まれています(要素のJSON構造が変化していることを意味します)。唯一の共通の機能は、タイプを区別するために使用できるtypeフィールドです。その他のフィールドはすべて異なります。例:Argonaut:多相配列のデコード

{ 
    ..., 
    array: [ 
     { type: "a", a1: ..., a2: ...}, 
     { type: "b", b1: ...}, 
     { type: "c", c1: ..., c2: ..., c3: ...}, 
     { type: "a", a1: ..., a2: ...}, 
     ... 
    ], 
    ... 
} 

アルゴノートを使用して、JSONの配列をマッピングすることが可能であるElementのようにタイプElementAElementBの適切な場合クラスのスーパータイプであり、スカラSeq[Element]

私はplay-jsonと同じことをしたし、それが(typeフィールドを評価し、それに応じて、より具体的なReadsに転送し、基本的Reads[Element])は非常に簡単でした。しかし、私はargonautでこれを行う方法を見つけることができませんでした。


編集:例

Scalaの種類(私が使用したい):

case class Container(id: Int, events: List[Event]) 

sealed trait Event 
case class Birthday(name: String, age: Int) extends Event 
case class Appointment(start: Long, participants: List[String]) extends Event 
case class ... extends Event 

JSONインスタンス(ない私の制御下):

{ 
    "id":1674, 
    "events": { 
     "data": [ 
     { 
      "type": "birthday", 
      "name": "Jones Clinton", 
      "age": 34 
     }, 
     { 
      "type": "appointment", 
      "start": 1675156665555, 
      "participants": [ 
       "John Doe", 
       "Jane Doe", 
       "Foo Bar" 
      ] 
     } 
     ] 
    } 
} 

答えて

2

あなたはすることができますこの形式を扱うデコーダを構築するのに役立つ小さな関数を作成します。

例を参照してください。

import argonaut._, Argonaut._ 

def decodeByType[T](encoders: (String, DecodeJson[_ <: T])*) = { 
    val encMap = encoders.toMap 

    def decoder(h: CursorHistory, s: String) = 
    encMap.get(s).fold(DecodeResult.fail[DecodeJson[_ <: T]](s"Unknown type: $s", h))(d => DecodeResult.ok(d)) 

    DecodeJson[T] { c: HCursor => 
    val tf = c.downField("type") 

    for { 
     tv <- tf.as[String] 
     dec <- decoder(tf.history, tv) 
     data <- dec(c).map[T](identity) 
    } yield data 
    } 
} 

case class Container(id: Int, events: ContainerData) 
case class ContainerData(data: List[Event]) 

sealed trait Event 
case class Birthday(name: String, age: Int) extends Event 
case class Appointment(start: Long, participants: List[String]) extends Event 

implicit val eventDecoder: DecodeJson[Event] = decodeByType[Event](
    "birthday" -> DecodeJson.derive[Birthday], 
    "appointment" -> DecodeJson.derive[Appointment] 
) 

implicit val containerDataDecoder: DecodeJson[ContainerData] = DecodeJson.derive[ContainerData] 
implicit val containerDecoder: DecodeJson[Container] = DecodeJson.derive[Container] 

val goodJsonStr = 
    """ 
    { 
     "id":1674, 
     "events": { 
      "data": [ 
      { 
       "type": "birthday", 
       "name": "Jones Clinton", 
       "age": 34 
      }, 
      { 
       "type": "appointment", 
       "start": 1675156665555, 
       "participants": [ 
        "John Doe", 
        "Jane Doe", 
        "Foo Bar" 
       ] 
      } 
      ] 
     } 
    } 
    """ 

def main(args: Array[String]) = { 
    println(goodJsonStr.decode[Container]) 

    // \/-(Container(1674,ContainerData(List(Birthday(Jones Clinton,34), Appointment(1675156665555,List(John Doe, Jane Doe, Foo Bar)))))) 
} 
+0

ご回答ありがとうございます。問題は、単一のサブオブジェクト( 'data'など)がなく、すべてのペイロードが同じ階層レベルにあることです。私は私の質問の例を編集しました。 – ceran

+0

さて、あなたが言っていることが分かります。オリジナルの投稿を、必要なものを処理する新しいデコーダで更新しました。 – longshorej

+0

あなたの時間を無駄にして申し訳ありません。私のクラスはちょっと違って見えるし、それを動作させることができませんでした。たぶん私たちは最終的な試みを始めることができます - 私は上記の完全な例を追加しました。本当にありがとう。 – ceran