Jackson-일반 클래스를 사용하여 직렬화 해제


답변:


238

사용하는 TypeReference각 제네릭 형식에 대한 개체 를 생성하고 역 직렬화에 사용해야합니다. 예를 들어-

mapper.readValue(jsonString, new TypeReference<Data<String>>() {});

TypeReference <Data <T >> () {}로 사용해야합니다 ...하지만 다음과 같은 오류가 발생합니다. java.lang.class에서 개인 java.lang.class.Class ()에 액세스 할 수 없습니다. 액세스를 설정하지 못했습니다. java.lang.Class 생성자를 액세스 가능하게 만들 수 없음
gnjago

아니요, 아닙니다 Data<T>. 유형이 아닙니다. 실제 클래스를 지정해야합니다. 그렇지 않으면와 동일합니다 Data<Object>.
StaxMan

18
런타임까지 클래스가 무엇인지 모른다면 어떻게해야합니까? 런타임 중에 클래스를 매개 변수로 가져옵니다. 이 공개 <T> void deSerialize (Class <T> clazz {ObjectMapper mapper = new ObjectMapper (); mapper.readValue (jsonString, new TypeReference <Json <T >> () {}));}
gnjago

1
나는 여기에 전체 질문을 올바르게 물었다 stackoverflow.com/questions/11659844/…
gnjago

전체 패키지 이름은 TypeReference무엇입니까? 그렇 com.fasterxml.jackson.core.type습니까?
레이 양

88

그렇게 할 수 없습니다 :과 같이 완전히 해결 된 유형을 지정해야합니다 Data<MyType>. T변수 일 뿐이며 의미가 없습니다.

그러나 그것이 T정적으로 알려진 것이 아니라는 것을 의미한다면 , TypeReference동적으로 동등한 것을 만들어야합니다 . 언급 된 다른 질문은 이미 이것을 언급 할 수 있지만 다음과 같이 보일 것입니다.

public Data<T> read(InputStream json, Class<T> contentClass) {
   JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
   return mapper.readValue(json, type);
}

2
런타임까지 클래스가 무엇인지 모른다면 어떻게해야합니까? 런타임 중에 클래스를 매개 변수로 가져옵니다. 이 공용 추천 <T> 공극 직렬화 (클래스 <T> {clazz에 ObjectMapper 매퍼 = 새로운 ObjectMapper (); mapper.readValue (jsonString 새로운 TypeReference <JSON <T >> () {})}
gnjago

1
여기에 전체 질문이 있습니다. stackoverflow.com/questions/11659844/…
gnjago

2
그런 다음 필요하지 않은 클래스를 그대로 전달하십시오 TypeReference. return mapper.readValue(json, clazz);정확히 무엇이 문제입니까?
StaxMan

2
문제는 "데이터"가 일반 클래스라는 것입니다. 런타임에 T 유형이 무엇인지 지정해야합니다. 매개 변수 clazz는 런타임시 사용됩니다. 그래서 readValue를 호출하는 방법은 무엇입니까? 새로운 TypeReference> Json <T >>로 호출하지 않습니다. 전체 질문은 여기에 있습니다. stackoverflow.com/questions/11659844/…
gnjago

2
확인. 그런 다음 TypeFactory.. 를 사용해야합니다 . 답변을 편집하겠습니다.
StaxMan

30

먼저 직렬화를 수행 한 다음 직렬화 해제를 수행 할 수 있습니다.
따라서 직렬화를 수행 할 때 @JsonTypeInfoJackson이 클래스 정보를 json 데이터에 쓰도록 해야합니다 . 당신이 할 수있는 일은 다음과 같습니다

Class Data <T> {
    int found;
    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
    Class<T> hits
}

그런 다음 deserialize하면 Jackson이 변수 적중이 실제로 런타임에 클래스로 데이터를 deserialize 한 것을 알 수 있습니다.


작동하지 않음, 오류 com.fasterxml.jackson.databind.exc.InvalidTypeIdException이 발생 함 property 'data')
gaurav9620

15

클래스 Data <>

ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)

이것은 더 이상 사용되지 않습니다.
에반 게르 티스

13

Util 클래스에서 정적 메소드를 작성하십시오. 파일에서 Json을 읽고 있습니다. 당신은 또한 readValue에 문자열을 줄 수 있습니다

public static <T> T convertJsonToPOJO(String filePath, Class<?> target) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(new File(filePath), objectMapper .getTypeFactory().constructCollectionType(List.class, Class.forName(target.getName())));
}

용법:

List<TaskBean> list =  Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);

7

일반 유형의 유형을 알고있는 다른 클래스로 랩핑 할 수 있습니다.

예 :

class Wrapper {
 private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);

여기에 구체적인 유형이 있습니다. 당신은 reified type마다 wrapper가 필요합니다. 그렇지 않으면 Jackson은 어떤 객체를 만들지 알지 못합니다.


6

역 직렬화해야하는 JSON 문자열에는 parameter에 대한 유형 정보가 포함되어야합니다 T.
Jackson이 매개 변수 유형에 대한 유형 정보 를 JSON 문자열에서 읽거나 쓸 수 있도록 매개 변수 T로 클래스에 전달할 수있는 모든 클래스에 Jackson 주석을 넣어야 합니다.DataT

T추상 클래스를 확장하는 모든 클래스가 될 수 있다고 가정 해 봅시다 Result.

class Data <T extends Result> {
    int found;
    Class<T> hits
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
        @JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"),
        @JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")})
public abstract class Result {

}

public class ImageResult extends Result {

}

public class NewsResult extends Result {

}

매개 변수로 전달 될 수있는 각 클래스 (또는 공통 수퍼 타입) T 에 주석을 달면 Jackson은 매개 변수 T에 대한 정보를 JSON에 포함합니다. 그런 다음 T컴파일 타임에 매개 변수 를 몰라도 이러한 JSON을 직렬화 해제 할 수 있습니다 .
Jackson 문서 링크 는 다형성 역 직렬화에 대해 설명하지만이 질문을 참조하는 데 유용합니다.


List를 원하면 어떻게 관리합니까? List <ImageResult>
Luca Archidiacono

5

Jackson 2.5부터는이를 해결하는 우아한 방법 은 매개 변수화 된 클래스와 해당 매개 변수화 된 형식을 지정하여 Jackson을 확실하게 정의 할 수 있는 TypeFactory.constructParametricType (Class parametrized, Class ... parameterClasses) 메서드를 사용하는 것 JavaType입니다.

역 직렬화하려는 Data<String>경우 다음을 수행 할 수 있습니다.

// the json variable may be a String, an InputStream and so for...
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class);
Data<String> data = mapper.readValue(json, type);

클래스가 여러 매개 변수화 된 유형을 선언 한 경우 실제로 어렵지 않습니다.

class Data <T, U> {
    int found;
    Class<T> hits;
    List<U> list;
}

우리는 할 수 있습니다 :

JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);

굉장해, 고맙습니다. typereference를 사용하여 map에서 특정 객체로 클래스 캐스트 예외가 발생했지만 실제로 작업을 수행합니다.
Tacsiazuma

1
public class Data<T> extends JsonDeserializer implements ContextualDeserializer {
    private Class<T> cls;
    public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty prop) throws JsonMappingException {
        cls = (Class<T>) ctx.getContextualType().getRawClass();
        return this;
    }
    ...
 }

0

스칼라를 사용하고 컴파일 타임에 제네릭 형식을 알고 있지만 모든 응용 프로그램의 모든 곳에서 TypeReference를 수동으로 전달하고 싶지 않은 경우 다음 코드를 사용할 수 있습니다 (jackson 2.9.5).

def read[T](entityStream: InputStream)(implicit typeTag: WeakTypeTag[T]): T = {

    //nathang: all of this *crazy* scala reflection allows us to handle List[Seq[Map[Int,Value]]]] without passing
    // new TypeReference[List[Seq[Map[Int,Value]]]]](){} to the function

    def recursiveFindGenericClasses(t: Type): JavaType = {
      val current = typeTag.mirror.runtimeClass(t)

      if (t.typeArgs.isEmpty) {
        val noSubtypes = Seq.empty[Class[_]]
        factory.constructParametricType(current, noSubtypes:_*)
      }

      else {
        val genericSubtypes: Seq[JavaType] = t.typeArgs.map(recursiveFindGenericClasses)
        factory.constructParametricType(current, genericSubtypes:_*)
      }

    }

    val javaType = recursiveFindGenericClasses(typeTag.tpe)

    json.readValue[T](entityStream, javaType)
  }

다음과 같이 사용할 수 있습니다.

read[List[Map[Int, SomethingToSerialize]]](inputStream)

0

Jackson을 사용하여 일반 JSON 문자열을 Java 객체로 직렬화 해제하려면 다음이 필요합니다.

  1. JSON 클래스를 정의합니다.

  2. 속성 매핑을 수행하십시오.

최종 코드, 테스트 및 사용 준비 완료 :

static class MyJSON {

    private Map<String, Object> content = new HashMap<>();

    @JsonAnySetter
    public void setContent(String key, Object value) {
        content.put(key, value);
    }
}

String json = "{\"City\":\"Prague\"}";

try {

    MyPOJO myPOJO = objectMapper.readValue(json, MyPOJO.class);

    String jsonAttVal = myPOJO.content.get("City").toString();

    System.out.println(jsonAttVal);

} catch (IOException e) {
    e.printStackTrace();
}

중요 :
@JsonAnySetter 주석은 필수이며 일반적인 JSON 구문 분석 및 채우기를 보장합니다.

중첩 배열이있는 복잡한 경우 Baeldung 참조를 참조하십시오 : https://www.baeldung.com/jackson-mapping-dynamic-object


0

아주 좋지는 않지만 간단한 결정의 예 (잭슨뿐만 아니라 Spring RestTemplate 등) :

Set<MyClass> res = new HashSet<>();
objectMapper.readValue(json, res.getClass());
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.