Java 如何从 Jackson 中的自定义解串器调用默认解串器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18313323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I call the default deserializer from a custom deserializer in Hymanson
提问by Pablo Jomer
I have a problem in my custom deserializer in Hymanson. I want to access the default serializer to populate the object I am deserializing into. After the population I will do some custom things but first I want to deserialize the object with the default Hymanson behavior.
我在 Hymanson 中的自定义解串器有问题。我想访问默认序列化程序来填充我要反序列化的对象。在填充之后,我会做一些自定义的事情,但首先我想用默认的 Hymanson 行为反序列化对象。
This is the code that I have at the moment.
这是我目前拥有的代码。
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;
}
}
What I need is a way to initialize the default deserializer so that I can pre-populate my POJO before I start my special logic.
我需要的是一种初始化默认解串器的方法,以便我可以在开始我的特殊逻辑之前预先填充我的 POJO。
When calling deserialize from within the custom deserializer It seems the method is called from the current context no matter how I construct the serializer class. Because of the annotation in my POJO. This causes a Stack Overflow exception for obvious reasons.
从自定义反序列化器中调用反序列化时,无论我如何构造序列化器类,似乎该方法都是从当前上下文调用的。因为我的 POJO 中有注释。由于显而易见的原因,这会导致堆栈溢出异常。
I have tried initializing a BeanDeserializer
but the process is extremely complex and I haven't managed to find the right way to do it. I have also tried overloading the AnnotationIntrospector
to no avail, thinking that it might help me ignore the annotation in the DeserializerContext
. Finally it seams I might have had some success using JsonDeserializerBuilders
although this required me to do some magic stuff to get hold of the application context from Spring. I would appreciate any thing that could lead me to a cleaner solution for example how Can I construct a deserialization context without reading the JsonDeserializer
annotation.
我试过初始化 aBeanDeserializer
但这个过程非常复杂,我还没有找到正确的方法来做到这一点。我也试过重载AnnotationIntrospector
无济于事,认为它可能帮助我忽略DeserializerContext
. 最后它接缝我可能已经取得了一些成功,JsonDeserializerBuilders
尽管这需要我做一些神奇的事情来从 Spring 获取应用程序上下文。我很感激任何可以引导我找到更清晰解决方案的事情,例如如何在不阅读JsonDeserializer
注释的情况下构建反序列化上下文。
采纳答案by schummar
As StaxMan already suggested you can do this by writing a BeanDeserializerModifier
and registering it via SimpleModule
. The following example should work:
正如 StaxMan 已经建议您可以通过编写 aBeanDeserializerModifier
并通过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);
}
}
回答by StaxMan
There are couple of ways to do this, but to do it right involves bit more work. Basically you can not use sub-classing, since information default deserializers need is built from class definitions.
有几种方法可以做到这一点,但要正确地做到这一点需要做更多的工作。基本上你不能使用子类,因为默认反序列化器需要的信息是从类定义中构建的。
So what you can most likely use is to construct a BeanDeserializerModifier
, register that via Module
interface (use SimpleModule
). You need to define/override modifyDeserializer
, and for the specific case where you want to add your own logic (where type matches), construct your own deserializer, pass the default deserializer you are given.
And then in deserialize()
method you can just delegate call, take the result Object.
所以你最有可能使用的是构造一个BeanDeserializerModifier
, 通过Module
接口注册它(use SimpleModule
)。您需要定义/覆盖modifyDeserializer
,并且对于您想要添加自己的逻辑(类型匹配)的特定情况,构建您自己的解串器,传递给定的默认解串器。然后在deserialize()
方法中,您可以委托调用,获取结果对象。
Alternatively, if you must actually create and populate the object, you can do so and call overloaded version of deserialize()
that takes third argument; object to deserialize into.
或者,如果您必须实际创建和填充对象,您可以这样做并调用该对象的重载版本,deserialize()
该版本采用第三个参数;要反序列化的对象。
Another way that might work (but not 100% sure) would be to specify Converter
object (@JsonDeserialize(converter=MyConverter.class)
). This is a new Hymanson 2.2 feature.
In your case, Converter would not actually convert type, but simplify modify the object: but I don't know if that would let you do exactly what you want, since the default deserializer would be called first, and only then your Converter
.
另一种可能有效(但不是 100% 确定)的方法是指定Converter
对象 ( @JsonDeserialize(converter=MyConverter.class)
)。这是 Hymanson 2.2 的新功能。在您的情况下,Converter 不会实际转换类型,而是简化修改对象:但我不知道这是否会让您完全按照自己的意愿行事,因为将首先调用默认的反序列化器,然后才调用您的Converter
.
回答by Tomá? Zálusky
I was not ok with using BeanSerializerModifier
since it forces to declare some behavioral changes in central ObjectMapper
rather than in custom deserializer itself and in fact it is parallel solution to annotating entity class with JsonSerialize
. If you feel it the similar way, you might appreciate my answer here: https://stackoverflow.com/a/43213463/653539
我不BeanSerializerModifier
同意使用,因为它强制在中央ObjectMapper
而不是在自定义反序列化器本身中声明一些行为更改,实际上它是使用JsonSerialize
. 如果你有类似的感觉,你可能会喜欢我的回答:https: //stackoverflow.com/a/43213463/653539
回答by Michail Michailidis
A simpler solution for me was to just add another bean of ObjectMapper
and use that to deserialize the object (thanks to https://stackoverflow.com/users/1032167/varrencomment) - in my case I was interested to either deserialize to its id (an int) or the whole object https://stackoverflow.com/a/46618193/986160
对我来说,一个更简单的解决方案是添加另一个 beanObjectMapper
并使用它来反序列化对象(感谢https://stackoverflow.com/users/1032167/varren评论) - 在我的情况下,我有兴趣反序列化为其 id (int) 或整个对象https://stackoverflow.com/a/46618193/986160
import com.fasterxml.Hymanson.annotation.JsonAutoDetect;
import com.fasterxml.Hymanson.annotation.PropertyAccessor;
import com.fasterxml.Hymanson.core.JsonParser;
import com.fasterxml.Hymanson.core.JsonProcessingException;
import com.fasterxml.Hymanson.databind.*;
import com.fasterxml.Hymanson.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;
}
}
for each entity that needs to be going through custom deserializer we need to configure it in the global ObjectMapper
bean of the Spring Boot App in my case (e.g for Category
):
对于需要通过自定义反序列化器的每个实体,我们需要ObjectMapper
在 Spring Boot 应用程序的全局bean 中对其进行配置(例如 for 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;
}
回答by hopper
Along the lines of what Tomá? Zálusky has suggested, in cases where using BeanDeserializerModifier
is undesirable you can construct a default deserializer yourself using BeanDeserializerFactory
, although there is some extra setup necessary. In context, this solution would look like so:
沿着什么Tomá的路线?Zálusky 建议,在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;
}
回答by Bill
If it is possible for you to declare extra User class then you can implement it just using annotations
如果您可以声明额外的 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;
}
}
回答by Derek Cochran
The DeserializationContext
has a readValue()
method you may use. This should work for both the default deserializer and any custom deserializers you have.
该DeserializationContext
有一个readValue()
你可以使用的方法。这应该适用于默认解串器和您拥有的任何自定义解串器。
Just be sure to call traverse()
on the JsonNode
level you want to read to retrieve the JsonParser
to pass to readValue()
.
只需确保调用您要读取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;
}
}
回答by Gili
I found an answer at https://stackoverflow.com/a/51927577/14731which is much more readable than the accepted answer.
我在https://stackoverflow.com/a/51927577/14731找到了一个答案,它比接受的答案更具可读性。
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode tree = jp.readValueAsTree();
// To call the default deserializer, simply invoke:
User user = tree.get("user").traverse(jp.getCodec()).readValueAs(User.class);
return user;
}
It really doesn't get easier than this.
真的没有比这更容易的了。
回答by oberlies
You are bound to fail if you try to create your custom deserializer from scratch.
如果您尝试从头开始创建自定义反序列化器,您一定会失败。
Instead, you need to get hold of the (fully configured) default deserializer instance through a custom BeanDeserializerModifier
, and then pass this instance to your custom deserializer class:
相反,您需要通过 custom 获取(完全配置的)默认解串器实例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;
}
Note: This module registration replaces the @JsonDeserialize
annotation, i.e. the User
class or User
fields should no longer be annotated with this annotation.
注意:此模块注册替换了@JsonDeserialize
注解,即User
类或User
字段不应再使用此注解进行注解。
The custom deserializer should then be based on a DelegatingDeserializer
so that all methods delegate, unless you provide an explicit implementation:
自定义反序列化器应该基于 aDelegatingDeserializer
以便所有方法委托,除非您提供显式实现:
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;
}
}
回答by kaiser
Here is a oneliner using ObjectMapper
这是一个使用 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;
}
And please: There is really no need to use any String value or something else. All needed information are given by JsonParser, so use it.
并且请:真的没有必要使用任何字符串值或其他东西。JsonParser 提供了所有需要的信息,因此请使用它。