![](/img/trans.png)
[英]How do I call the default deserializer in a custom deserializer in Jackson?
[英]How do I call the default deserializer from a custom deserializer in Jackson
我在 Jackson 中的自定義解串器有問題。 我想訪問默認序列化程序來填充我要反序列化的對象。 在填充之后,我會做一些自定義的事情,但首先我想用默認的 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。
從自定義反序列化器中調用反序列化時,無論我如何構造序列化器類,似乎該方法都是從當前上下文調用的。 因為我的 POJO 中有注釋。 由於顯而易見的原因,這會導致堆棧溢出異常。
我嘗試過初始化一個BeanDeserializer
但這個過程非常復雜,我還沒有找到正確的方法來做到這一點。 我也嘗試過重載AnnotationIntrospector
無濟於事,認為它可能會幫助我忽略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);
}
}
DeserializationContext
有一個你可以使用的readValue()
方法。 這應該適用於默認解串器和您擁有的任何自定義解串器。
請務必在要讀取的JsonNode
級別上調用traverse()
以檢索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;
}
}
我在ans找到了一個比公認的答案更具可讀性的答案。
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
User user = jp.readValueAs(User.class);
// some code
return user;
}
真的沒有比這更容易的了。
有幾種方法可以做到這一點,但要正確地做到這一點需要做更多的工作。 基本上你不能使用子類,因為默認反序列化器需要的信息是從類定義中構建的。
所以你最有可能使用的是構造一個BeanDeserializerModifier
,通過Module
接口注冊它(使用SimpleModule
)。 您需要定義/覆蓋modifyDeserializer
,並且對於您想要添加自己的邏輯(類型匹配)的特定情況,構造您自己的反序列化器,傳遞給定的默認反序列化器。 然后在deserialize()
方法中,您可以委托調用,獲取結果對象。
或者,如果您必須實際創建和填充對象,您可以這樣做並調用deserialize()
第三個參數的deserialize()
重載版本; 要反序列化的對象。
另一種可能有效(但不是 100% 確定)的方法是指定Converter
對象( @JsonDeserialize(converter=MyConverter.class)
)。 這是 Jackson 2.2 的新功能。 在您的情況下, Converter 實際上不會轉換類型,而是簡化修改對象:但我不知道這是否會讓您完全按照自己的意願行事,因為將首先調用默認解串器,然后才調用您的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;
}
}
按照Tomáš 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;
}
我不同意使用BeanSerializerModifier
因為它強制在中央ObjectMapper
而不是在自定義反序列化器本身中聲明一些行為更改,實際上它是使用JsonSerialize
注釋實體類的並行解決方案。 如果你有類似的感覺,你可能會喜歡我的回答: https : //stackoverflow.com/a/43213463/653539
如果您嘗試從頭開始創建自定義反序列化器,您一定會失敗。
相反,您需要通過自定義BeanDeserializerModifier
獲取(完全配置的)默認反序列化器實例,然后將此實例傳遞給您的自定義反序列化器類:
public ObjectMapper getMapperWithCustomDeserializer() {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription beanDesc, JsonDeserializer<?> defaultDeserializer)
if (beanDesc.getBeanClass() == User.class) {
return new UserEventDeserializer(defaultDeserializer);
} else {
return defaultDeserializer;
}
}
});
objectMapper.registerModule(module);
return objectMapper;
}
注意:此模塊注冊替換了@JsonDeserialize
注解,即User
類或User
字段不應再使用此注解進行注解。
然后自定義反序列化器應該基於DelegatingDeserializer
以便所有方法都委托,除非您提供顯式實現:
public class UserEventDeserializer extends DelegatingDeserializer {
public UserEventDeserializer(JsonDeserializer<?> delegate) {
super(delegate);
}
@Override
protected JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegate) {
return new UserEventDeserializer(newDelegate);
}
@Override
public User deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
User result = (User) super.deserialize(p, ctxt);
// add special logic here
return result;
}
}
這是一個使用 ObjectMapper 的 oneliner
public MyObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
MyObject object = new ObjectMapper().readValue(p, MyObject.class);
// do whatever you want
return object;
}
並且請:真的沒有必要使用任何字符串值或其他東西。 JsonParser 提供了所有需要的信息,因此請使用它。
使用BeanDeserializerModifier
效果很好,但如果您需要使用JsonDeserialize
有一種方法可以使用AnnotationIntrospector
來做到這一點,如下所示:
ObjectMapper originalMapper = new ObjectMapper();
ObjectMapper copy = originalMapper.copy();//to keep original configuration
copy.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
@Override
public Object findDeserializer(Annotated a) {
Object deserializer = super.findDeserializer(a);
if (deserializer == null) {
return null;
}
if (deserializer.equals(MyDeserializer.class)) {
return null;
}
return deserializer;
}
});
現在復制的映射器現在將忽略您的自定義反序列化器 (MyDeserializer.class) 並使用默認實現。 您可以在自定義反deserialize
方法中使用它,通過將復制的映射器設為靜態或在使用 Spring 時連接它來避免遞歸。
對我來說,一個更簡單的解決方案是添加另一個ObjectMapper
bean 並使用它來反序列化對象(感謝https://stackoverflow.com/users/1032167/varren評論) - 在我的情況下,我有興趣要么反序列化到它的id(整數)或整個對象https://stackoverflow.com/a/46618193/986160
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.springframework.context.annotation.Bean;
import java.io.IOException;
public class IdWrapperDeserializer<T> extends StdDeserializer<T> {
private Class<T> clazz;
public IdWrapperDeserializer(Class<T> clazz) {
super(clazz);
this.clazz = clazz;
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
return mapper;
}
@Override
public T deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
String json = jp.readValueAsTree().toString();
// do your custom deserialization here using json
// and decide when to use default deserialization using local objectMapper:
T obj = objectMapper().readValue(json, clazz);
return obj;
}
}
對於需要通過自定義反序列化器的每個實體,我們需要在 Spring Boot App 的全局ObjectMapper
bean 中對其進行配置(例如Category
):
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
SimpleModule testModule = new SimpleModule("MyModule")
.addDeserializer(Category.class, new IdWrapperDeserializer(Category.class))
mapper.registerModule(testModule);
return mapper;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.