2016-11-22 6 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 등등 형 ElementA, ElementB의 경우 적절한 클래스의 슈퍼 스칼라이고 Seq[Element]?

나는 play-json과 같은 일을했고 그것은 (기본적으로 따라 type 필드를 평가하고 Reads[Element]Reads에 대한보다 구체적인 전달) 매우 쉬웠다. 그러나 나는 argonaut와 함께 이것을 할 수있는 방법을 찾을 수 없었다.


편집 : 예

스칼라 유형 (내가 사용하고자하는) : (나의 통제하에)

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