Jackson의 사용자 지정 deserializer에 문제가 있습니다. 기본 직렬 변환기에 액세스하여 역 직렬화중인 개체를 채우고 싶습니다. 채우기 후에 몇 가지 사용자 지정 작업을 수행하지만 먼저 기본 Jackson 동작으로 개체를 역 직렬화하려고합니다.
이것이 제가 현재 가지고있는 코드입니다.
public class UserEventDeserializer extends StdDeserializer<User> {
private static final long serialVersionUID = 7923585097068641765L;
public UserEventDeserializer() {
super(User.class);
}
@Override
@Transactional
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
User deserializedUser = null;
deserializedUser = super.deserialize(jp, ctxt, new User());
// The previous line generates an exception java.lang.UnsupportedOperationException
// Because there is no implementation of the deserializer.
// I want a way to access the default spring deserializer for my User class.
// How can I do that?
//Special logic
return deserializedUser;
}
}
필요한 것은 기본 디시리얼라이저를 초기화하여 특수 로직을 시작하기 전에 POJO를 미리 채울 수있는 방법입니다.
사용자 지정 deserializer 내에서 deserialize를 호출 할 때 serializer 클래스를 구성하는 방법에 관계없이 현재 컨텍스트에서 메서드가 호출되는 것 같습니다. 내 POJO의 주석 때문입니다. 이로 인해 명백한 이유로 스택 오버플로 예외가 발생합니다.
나는 초기화를 시도 BeanDeserializer
했지만 프로세스가 매우 복잡하고 올바른 방법을 찾지 못했습니다. 또한 AnnotationIntrospector
.NET 파일의 주석을 무시하는 데 도움이 될 수 있다고 생각하면서을 (를) 사용하지 않도록 오버로드 해 보았습니다 DeserializerContext
. 마지막으로 JsonDeserializerBuilders
Spring에서 응용 프로그램 컨텍스트를 확보하기 위해 몇 가지 마법 작업을 수행해야했지만 성공했을 수도 있습니다 . 예를 들어 JsonDeserializer
주석 을 읽지 않고 역 직렬화 컨텍스트를 구성하는 방법과 같이 더 깨끗한 솔루션으로 이끌 수있는 모든 것을 감사하겠습니다 .
답변
StaxMan이 이미 제안했듯이을 작성하고을 BeanDeserializerModifier
통해 등록하여이를 수행 할 수 있습니다 SimpleModule
. 다음 예제가 작동합니다.
public class UserEventDeserializer extends StdDeserializer<User> implements ResolvableDeserializer
{
private static final long serialVersionUID = 7923585097068641765L;
private final JsonDeserializer<?> defaultDeserializer;
public UserEventDeserializer(JsonDeserializer<?> defaultDeserializer)
{
super(User.class);
this.defaultDeserializer = defaultDeserializer;
}
@Override public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException
{
User deserializedUser = (User) defaultDeserializer.deserialize(jp, ctxt);
// Special logic
return deserializedUser;
}
// for some reason you have to implement ResolvableDeserializer when modifying BeanDeserializer
// otherwise deserializing throws JsonMappingException??
@Override public void resolve(DeserializationContext ctxt) throws JsonMappingException
{
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException
{
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier()
{
@Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer)
{
if (beanDesc.getBeanClass() == User.class)
return new UserEventDeserializer(deserializer);
return deserializer;
}
});
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
User user = mapper.readValue(new File("test.json"), User.class);
}
}
답변
나는 받아 들여진 대답보다 훨씬 더 읽기 쉬운 ans 에서 대답을 찾았다 .
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
User user = jp.readValueAs(User.class);
// some code
return user;
}
이보다 더 쉽지는 않습니다.
답변
에 사용할 수 DeserializationContext
있는 readValue()
방법이 있습니다. 이것은 기본 deserializer와 사용자 지정 deserializer 모두에서 작동합니다.
그냥 호출해야합니다 traverse()
온 JsonNode
당신이 검색 읽고 싶은 수준 JsonParser
에 전달할 readValue()
.
public class FooDeserializer extends StdDeserializer<FooBean> {
private static final long serialVersionUID = 1L;
public FooDeserializer() {
this(null);
}
public FooDeserializer(Class<FooBean> t) {
super(t);
}
@Override
public FooBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
FooBean foo = new FooBean();
foo.setBar(ctxt.readValue(node.get("bar").traverse(), BarBean.class));
return foo;
}
}
답변
이를 수행하는 몇 가지 방법이 있지만 올바르게 수행하려면 약간의 작업이 필요합니다. 기본적으로 기본 디시리얼라이저에 필요한 정보는 클래스 정의에서 빌드되기 때문에 하위 클래스를 사용할 수 없습니다.
따라서 가장 많이 사용할 수있는 것은를 생성하고 인터페이스 BeanDeserializerModifier
를 통해 등록하는 것입니다 Module
(사용 SimpleModule
). 정의 / 재정의해야하며 modifyDeserializer
, 고유 한 논리 (유형이 일치하는 위치)를 추가하려는 특정 경우에 대해 고유 한 deserializer를 생성하고 제공된 기본 deserializer를 전달해야합니다. 그런 다음 deserialize()
메서드 에서 호출을 위임하고 결과 Object를 가져올 수 있습니다.
또는 실제로 개체를 만들고 채워야하는 경우 그렇게하여 deserialize()
세 번째 인수 를 사용 하는 오버로드 된 버전을 호출 할 수 있습니다 . 역 직렬화 할 개체입니다.
작동 할 수있는 또 다른 방법 (100 % 확실하지는 않음)은 Converter
객체 ( @JsonDeserialize(converter=MyConverter.class)
) 를 지정하는 것 입니다. 이것은 새로운 Jackson 2.2 기능입니다. 귀하의 경우 Converter는 실제로 유형을 변환하지 않고 객체를 간단하게 수정합니다.하지만 기본 deserializer가 먼저 호출되고 Converter
.
답변
추가 User 클래스를 선언 할 수있는 경우 주석을 사용하여 구현할 수 있습니다.
// your class
@JsonDeserialize(using = UserEventDeserializer.class)
public class User {
...
}
// extra user class
// reset deserializer attribute to default
@JsonDeserialize
public class UserPOJO extends User {
}
public class UserEventDeserializer extends StdDeserializer<User> {
...
@Override
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// specify UserPOJO.class to invoke default deserializer
User deserializedUser = jp.ReadValueAs(UserPOJO.class);
return deserializedUser;
// or if you need to walk the JSON tree
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = oc.readTree(jp);
// specify UserPOJO.class to invoke default deserializer
User deserializedUser = mapper.treeToValue(node, UserPOJO.class);
return deserializedUser;
}
}
답변
무엇의 라인을 따라 토마시 Záluský 제안했다 사용하는 경우, BeanDeserializerModifier
사용 자신 디시리얼라이저 기본을 구성 할 수 있습니다 바람직하지 않다 BeanDeserializerFactory
필요한 몇 가지 추가 설정이 있지만,. 맥락에서이 솔루션은 다음과 같습니다.
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
User deserializedUser = null;
DeserializationConfig config = ctxt.getConfig();
JavaType type = TypeFactory.defaultInstance().constructType(User.class);
JsonDeserializer<Object> defaultDeserializer = BeanDeserializerFactory.instance.buildBeanDeserializer(ctxt, type, config.introspect(type));
if (defaultDeserializer instanceof ResolvableDeserializer) {
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
JsonParser treeParser = oc.treeAsTokens(node);
config.initialize(treeParser);
if (treeParser.getCurrentToken() == null) {
treeParser.nextToken();
}
deserializedUser = (User) defaultDeserializer.deserialize(treeParser, context);
return deserializedUser;
}
답변
다음은 ObjectMapper를 사용하는 oneliner입니다.
public MyObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
OMyObject object = new ObjectMapper().readValue(p, MyObject.class);
// do whatever you want
return object;
}
그리고 제발 : 정말 어떤 문자열 값이나 다른 것을 사용할 필요가 없습니다. 필요한 모든 정보는 JsonParser에서 제공하므로 사용하십시오.