2016-11-22 4 views
3

Das JSON-Objekt, für das ich versuche, ein DecodeJson[T] zu schreiben, enthält ein Array von verschiedenen "Typen" (was bedeutet, dass die JSON-Struktur seiner Elemente variiert). Das einzige gemeinsame Merkmal ist das Feld type, mit dem zwischen den Typen unterschieden werden kann. Alle anderen Felder sind unterschiedlich. Beispiel:Argonaut: Decodieren eines polymorphen Arrays

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

Mit argonaut, ist es möglich, den JSON-Array zu einer Scala zur Karte Seq[Element] wo Element ist ein übergeordneter Typ von geeigneten Fallklassen des Typs ElementA, ElementB und so weiter?

tat ich das Gleiche mit play-json und es war recht einfach (im Grunde ein Reads[Element], die das type Feld auswertet und spezifischere Reads weiterleitet entsprechend). Ich konnte jedoch keinen Weg finden, dies mit Argonaut zu tun.


edit: Beispiel

Scala Typen (Ich möchte verwenden):

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-Instanz (nicht unter meiner Kontrolle):

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

Antwort

2

Sie können Erstellen Sie eine kleine Funktion, mit der Sie einen Decoder erstellen können, der dieses Format verarbeitet.

Siehe unten für ein Beispiel.

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

Vielen Dank für Ihre Antwort. Das Problem besteht darin, dass es kein einzelnes Unterobjekt (wie zum Beispiel "Daten") gibt, sondern dass sich die gesamte Nutzlast auf derselben hierarchischen Ebene befindet. Ich habe das Beispiel in meiner Frage bearbeitet. – ceran

+0

Okay, ich verstehe was du sagst. Ich habe meinen ursprünglichen Beitrag mit einem neuen Decoder aktualisiert, der mit allem was Sie benötigen, umgehen kann. – longshorej

+0

Entschuldigung für die Verschwendung Ihrer Zeit. Meine Klassen sehen ein bisschen anders aus und ich konnte es nicht funktionieren lassen. Vielleicht können wir einen letzten Versuch starten - ich habe oben ein vollständiges Beispiel hinzugefügt. Vielen Dank. – ceran