我在杰克逊的自定义反序列化器中遇到了问题。我想访问默认的序列化程序来填充我反序列化的对象。在人口之后,我将做一些自定义的事情,但首先我想用默认的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。
从自定义反序列化器中调用deserialize时似乎从当前上下文调用该方法,无论我如何构造序列化器类。因为我的POJO中有注释。这会导致Stack Overflow异常,原因很明显。
我尝试初始化BeanDeserializer
,但这个过程非常复杂,我没有找到正确的方法来做到这一点。我也试过重载AnnotationIntrospector
无济于事,认为它可以帮助我忽略DeserializerContext
中的注释。最后,我可能使用JsonDeserializerBuilders
获得了一些成功,尽管这需要我做一些神奇的事情来掌握Spring的应用程序上下文。我将不胜感激,这可能会让我找到一个更清晰的解决方案,例如如何在不阅读JsonDeserializer
注释的情况下构建反序列化上下文。
答案 0 :(得分:79)
正如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);
}
}
答案 1 :(得分:8)
有几种方法可以做到这一点,但要做到这一点涉及更多的工作。基本上你不能使用子类,因为信息默认的反序列化需要是从类定义构建的。
所以你最有可能使用的是构造一个BeanDeserializerModifier
,通过Module
接口注册(使用SimpleModule
)。您需要定义/覆盖modifyDeserializer
,并且对于您想要添加自己的逻辑(类型匹配)的特定情况,构造您自己的反序列化器,传递您给出的默认反序列化器。
然后在deserialize()
方法中,您可以委托调用,获取结果对象。
或者,如果您必须实际创建并填充对象,则可以执行此操作并调用带有第三个参数的deserialize()
的重载版本;反序列化的对象。
可能有效(但不是100%肯定)的另一种方法是指定Converter
对象(@JsonDeserialize(converter=MyConverter.class)
)。这是Jackson 2.2的新功能。
在你的情况下,转换器实际上不会转换类型,但是简化修改对象:但我不知道是否会让你完全按照自己的意愿行事,因为默认的反序列化器会先被调用,然后只有你的{{1 }}
答案 2 :(得分:7)
DeserializationContext
您可以使用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;
}
}
答案 3 :(得分:4)
如果您可以声明额外的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;
}
}
答案 4 :(得分:2)
我在https://stackoverflow.com/a/51927577/14731找到了一个答案,它比接受的答案更具可读性。
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode tree = jp.readTree(jp);
// To call the default deserializer, simply invoke:
User user = tree.get("user").traverse(jp.getCodec()).readValueAs(User.class);
return user;
}
真的没有比这容易的事情了。
答案 5 :(得分:1)
对我来说更简单的解决方案是添加另一个ObjectMapper
的bean并使用它来反序列化对象(感谢https://stackoverflow.com/users/1032167/varren注释) - 在我的情况下,我有兴趣反序列化为其id (一个int)或整个对象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;
}
答案 6 :(得分:0)
我不习惯使用BeanSerializerModifier
,因为它强制在中心ObjectMapper
而不是自定义反序列化器本身中声明一些行为更改,实际上它是使用{{1来注释实体类的并行解决方案}}。如果你觉得它的方式相似,你可能会在这里感谢我的回答:https://stackoverflow.com/a/43213463/653539
答案 7 :(得分:0)
与Tomáš Záluský has suggested一样,在不希望使用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();
JsonDeserializer<Object> defaultDeserializer = BeanDeserializerFactory.instance.buildBeanDeserializer(ctxt, User.class, config.introspect(User.class));
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;
}
答案 8 :(得分:0)
如果您尝试从头开始创建自定义解串器,则注定会失败。相反,您需要通过自定义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;
}
}
答案 9 :(得分:0)
这里是使用ObjectMapper的一个班轮
public MyObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
OMyObject object = new ObjectMapper().readValue(p, MyObject.class);
// do whatever you want
return object;
}
并且请:确实不需要使用任何String值或其他值。所有需要的信息都由JsonParser给出,因此请使用它。
答案 10 :(得分:0)
使用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对其进行连线。