2013-08-02 3 views
4

나는 형태로 다음과 같은 한 JSON이 있습니다잭슨 스칼라 JSON 직렬화 클래스를 구분하는

{ 
"inventory": [ 
      { 
     "productType": "someProduct1", 
     "details": { 
      "productId": "Some_id", 
      "description": "some description" 
     } 
     }, 
{ 
     "productType": "someProduct2", 
     "details": { 
      "productId": "Some_id", 
      "description":{"someKey":"somevalue"} 
     } 
    } 
] 
} 

나는 위의 JSON은 다음과 같이 직렬화하는 사례 클래스 :

case class Inventory(products:List[Product]) 
case class Product(productType:String,details:ProductDetails) 
abstract class ProductDetails 
case class ProductDetailsSimple(productId:String,description:String) extends ProductDetails 
case class ProductDetailsComplex(productId:String,description:Map[String,String]) extends ProductDetails 

나는를 다음과 같이 jackson-scala module를 사용하여 위의 JSON 문자열을 역 직렬화하기 :

val mapper = new ObjectMapper() with ScalaObjectMapper 
mapper.registerModule(DefaultScalaModule) 
mapper.readValue(jsonBody, classOf[Inventory]) 

을 내가 오류입니다"예기치 않은 토큰 (END_OBJECT), 예상 FIELD_NAME : 유형 ID를 포함하는 '@details'속성이 없습니다 (ProductDetails 클래스 용) \ n [출처 : [email protected]; 줄 : 9, 열 : 5] "

나는 jackson documentation on Polymorphic deserialization을 거쳐 언급했듯이 조합을 시도했지만 행운은 없었습니다. 여기서 내가 잘못하고있는 부분을 이해하고 싶습니다. 잭슨 모듈

+0

이 작업을 수행 했습니까? – NightWolf

답변

12

내가 여기에 해결하기 위해 몇 가지 별도의 문제가 있다고 생각, 그래서 별도의 세 가지 방법을 나열했습니다

TL;.. DR

하나 제대로 잭슨 다형성을 사용하거나, 귀하의 경우, 이동 보다 단순한 접근 방식으로 다형성의 필요성 제거 엠. 내 code on github을 참조하십시오.

1. 사용자 정의 디시리얼라이저

포맷 된 JSON은 다음과 같습니다 필드 productType 내 의견으로는, 잘못입니다

{ inventory: 
    [ { productType: 'someProduct1', 
     details: 
     { productId: 'Some_id', 
      description: 'some description' } }, 
    { productType: 'someProduct2', 
     details: 
     { productId: 'Some_id', 
      description: { someKey: 'somevalue' } 
     } 
    } 
    ] 
} 

하지만이 형식은 엄격한 요구 사항 인 경우 다음 당신은 당신의 자신의 디시리얼라이저를 쓸 수있는 productType 필드를보고 다른 구체적인 클래스를 인스턴스화합니다.

나는 이것이 내가 예제 코드를 작성하지 않은,하지만 난

2. 잭슨 다형성

당신을 역 직렬화/사용자 정의 직렬화에 대한 참조로 Joda date-time package을 좋아하는 최적의 솔루션이 될 것이라고 생각하지 않는다

타입 필드와 ProductDetails에서 Product을 분리 맞는지 :

case class Product(productType:String,details:ProductDetails) 

abstract class ProductDetails 

나는 당신이 혼란스러워했습니다 방법 잭슨의 다형성 데이터 형식 처리 작업 결과로 수업 설계를 복잡하게 생각합니다.

아마도 비즈니스 규칙에서는 제품에 "유형"이 있어야 할 수도 있습니다.이 경우 "친절"또는 기타 비 코드 레이블로 지정하고 ProductDetails에 입력하십시오.

그러나 "유형"이 유형 다형성을 얻으려는 시도에 포함 되었다면 올바른 방법이 아닙니다.

내가 스칼라 잭슨 다형성의 작업 예제로 아래에 포함했다 : 나는 ProductProductDetails 대 구분을 제거

/** 
* The types here are close to the original question types but use 
* Jackson annotations to mark the polymorphic JSON treatment. 
*/ 

import scala.Array 
import com.fasterxml.jackson.annotation.JsonSubTypes.Type 
import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo} 

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME, 
    include = JsonTypeInfo.As.PROPERTY, 
    property = "type") 
@JsonSubTypes(Array(
    new Type(value = classOf[ProductDetailsSimple], name = "simple"), 
    new Type(value = classOf[ProductDetailsComplex], name = "complex") 
)) 
abstract class Product 

case class ProductDetailsSimple(productId: String, description: String) extends Product 

case class ProductDetailsComplex(productId: String, description: Map[String, String]) extends Product 

case class PolymorphicInventory(products: List[Product]) 

주, 그래서 Inventory 지금 바로 Product의 목록으로. 나는 그들이 이름을 변경해야한다고 생각하지만, ProductDetailsSimpleProductDetailsComplex이라는 이름을 남겼습니다.

사용 예제 :

val inv = PolymorphicInventory(
    List(
    ProductDetailsSimple(productId="Some_id", description="some description"), 
    ProductDetailsComplex(productId="Some_id", description=Map("someKey" -> "somevalue")) 
) 
) 

val s = jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(inv) 
println("Polymorphic Inventory as JSON: "+s) 

출력 :

Polymorphic Inventory as JSON: { 
    "products" : [ { 
    "type" : "simple", 
    "productId" : "Some_id", 
    "description" : "some description" 
    }, { 
    "type" : "complex", 
    "productId" : "Some_id", 
    "description" : { 
     "someKey" : "somevalue" 
    } 
    } ] 
} 

3.

내가이 경우 다형성이 전혀 필요하지 않은 것을 제안 다형성을 제거하고 그 오류 "설명"을 별개의 의도가있는 필드 일 때 단일 문자열 또는 키/값 맵으로 만들려고합니다.

아마도 관련 데이터 레거시 문제 (이 경우 사용자 정의 deser 제안 참조)이 있지만, 데이터가 컨트롤에있는 경우, 나는 "간단 이동"투표 :

case class Product(productId: String, 
        description: String="", 
        attributes: Map[String, String]=Map.empty) 

case class PlainInventory(products: List[Product]) 

I의 더 " 스칼라-를 훌륭하게은 "그래서 값의 부재를 나타 내기 위해 Option를 사용 :

case class Product(productId: String, 
        description: Option[String]=None, 
        attributes: Option[Map[String, String]]=None) 

사용 예제 :

val inv = PlainInventory(
    List(
    Product(productId="Some_id", description=Some("some description")), 
    Product(productId="Some_id", attributes=Some(Map("someKey" -> "somevalue"))) 
) 
) 

val s = jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(inv) 
println("Plain Inventory as JSON: "+s) 

출력 :

Plain Inventory as JSON: { 
    "products" : [ { 
    "productId" : "Some_id", 
    "description" : "some description" 
    }, { 
    "productId" : "Some_id", 
    "attributes" : { 
     "someKey" : "somevalue" 
    } 
    } ] 
} 

github에 최소한의 코드 작업.

+0

두 번째 솔루션은 기존 JSON을 수정합니다. JSON 형식을 변경할 수 없을 때 어떻게 처리합니까? –

+0

"형식 지정"이란 말은 "구조"를 의미한다고 생각하십니까? 솔루션 1과 3에서 정확한 구조를 유지하는 것에 대해 논평했습니다. 기본적으로 솔루션 1을 사용한다고 말하면됩니다. 가능한 솔루션 2를 만들 수는 있지만, 잭슨이 아닌 친숙한 JSON과 정확하게 일치해야한다면 맞춤 디시리얼라이저가 기본입니다 내가 생각할 수있는 옵션. –

+0

동의 함. 그러나 당신은 그것이 최선의 해결책은 아니라고 언급했지만, 그것이 가장 좋은 해결책이라고 생각합니다! :) –