2013-01-08 3 views
3

중첩 된 다형성 형식을 deserialize하기 위해 Jakson을 사용하려고합니다. 내 최상위 형식이 다른 추상 형식이 아닌 클래스에 의해 확장 된 다른 다형성 형식을 참조 함을 의미합니다. 이것은 작동하지 않으며 예외가 발생합니다.중첩 된 다형성 형식을 deserializing하는 잭슨

다음은 내가하려는 일의 축소 된 예입니다.

package com.adfin; 

import junit.framework.TestCase; 
import org.codehaus.jackson.annotate.JsonSubTypes; 
import org.codehaus.jackson.annotate.JsonTypeInfo; 
import org.codehaus.jackson.map.ObjectMapper; 

import java.io.IOException; 

public class JaksonDouble extends TestCase { 

    @JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME, 
    include = JsonTypeInfo.As.PROPERTY, 
    property = "name" 
) 
    @JsonSubTypes({ 
    @JsonSubTypes.Type(value = SecondLevel.class, name = "SECOND") 
    }) 
    public static abstract class FirstLevel { 
    public abstract String getTestValue(); 
    } 

    @JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS, 
    include = JsonTypeInfo.As.PROPERTY, 
    property = "@class" 
) 
    public static abstract class SecondLevel extends FirstLevel { 

    } 

    public static class FinalLevel extends SecondLevel { 
    String test; 
    @Override public String getTestValue() { return test; } 
    } 

    public void testDoubleAbstract() throws IOException { 
    String testStr = "{ \"name\": \"SECOND\", \"@class\": \"com.adfin.JasksonDouble.FinalLevel\", \"test\": \"foo\"}"; 

    ObjectMapper mapper = new ObjectMapper(); 
    FirstLevel result = mapper.readValue(testStr, FirstLevel.class); 
    } 
} 

추상적 인 유형에 대해서는 표준 예외가 있습니다.

org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.adfin.JaksonDouble$SecondLevel, problem: abstract types can only be instantiated with additional type information at [Source: [email protected]; line: 1, column: 19] 

사용 사례를 설명해 드리겠습니다. 데이터 흐름을 설명하는 Json 문서가 있습니다. 나는 하나의 값에 대한 연산을 기술하는 "레벨 1"에 추상 타입을 가지고있다. 일반적인 작업을 구현하는 추상 클래스가 아닌 클래스를 파생시킵니다 (모든 클래스에 @JsonSubTypes 주석을 추가합니다).

"CUSTOM"이라는 특수한 @JsonSubTypes가 있습니다. 이것은 다른 누군가가 작성한 사용자 정의 연산을 나타내는 또 다른 추상 클래스이며 (일반 항아리 외부) "@class"속성을 사용하여 정규화 된 클래스 이름을 지정할 수 있습니다. Jakson 파서가 두 번째 lavel 클래스에서 @JsonTypeInfo 주석을 읽지 않는 것처럼 보입니다.

어떻게하면됩니까? 아니면 적어도이 유스 케이스를 어떻게 작동시킬 수 있을까요?

답변

2

정의가 엉망입니다. 두 개의 유형 식별자, 유형 이름 및 클래스를 사용하려고합니다. 이것은 의미가 없습니다. 하나의 방법이나 다른 방법을 선택해야합니다. 둘 다 선택할 수는 없습니다.

Java 클래스 이름을 유형 정보로 선택하면 이름을 생략하십시오. 또한 FirstLevel에 대해서만 @JsonTypeInfo을 입력하면됩니다. 하위 클래스는이 정의를 상속합니다.

논리 형식 이름을 사용하려면 클래스 속성을 삭제하십시오.또한 주석을 사용하거나 ObjectMapper을 통해 등록하여 하위 유형 목록을 지정해야합니다.

0

먼저 json의 클래스 이름이 잘못되었습니다. com.adfin.JasksonDouble $ FinalLevel, 점 대신 달러 여야합니다.

다음은 작동하는 코드입니다. 그러나 모든 하위 유형의 항목에 올바르게 응답하는지 확실하지 않습니다.

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") 
public static abstract class FirstLevel { 
    public abstract String getTestValue(); 
} 

다른 클래스의 주석을 제거하면 제대로 작동합니다.

그러나이 모든 것들은 상당히 복잡해 보입니다. 다른 라이브러리를 사용해 볼 수 있다면 Genson을보고 싶을 수도 있습니다. 다형성 유형 지원을 활성화하려면 Genson 인스턴스를 구성해야합니다. 또한 json을 생성하는 사람이라면 다른 것을 필요로하지 않습니다 (Gonson을 사용하여 다형 유형을 처리하는 데 필요한 json 스트림을 생성 할 수 있으므로). 여기

은 예입니다

// enable polymorphic types support 
Genson genson = new Genson.Builder().setWithClassMetadata(true).create(); 
// the @class must be first property in the json object 
String testStr = "{ \"@class\": \"com.adfin.JasksonDouble$FinalLevel\", \"test\": \"foo\"}"; 
FirstLevel result = genson.deserialize(testStr, FirstLevel.class); 
System.out.println(result.getTestValue()); 

Genson 또 다른 좋은 점은 당신에게 당신의 클래스에 대한 별칭을 등록 할 수있는 기능을 제공한다는 것입니다, 그래서 당신은 스트림에있는 모든 패키지 정보를 사용할 필요가 없습니다 . 다른 이점은 json 스트림을 데이터베이스에 저장하고 클래스를 다른 패키지로 옮기는 경우 앱의 별칭을 변경하고 DB에서 모든 json 스트림을 마이그레이션하지 않아야한다는 것입니다.

Genson genson = new Genson.Builder().setWithClassMetadata(true) 
            .addAlias("FinalLevel", FinalLevel.class) 
            .create(); 
// the @class must be first property in the json object 
String testStr = "{ \"@class\": \"FinalLevel\", \"test\": \"foo\"}"; 
FirstLevel result = genson.deserialize(testStr, FirstLevel.class); 
System.out.println(result.getTestValue());