提问人:Eni 提问时间:5/2/2023 更新时间:5/2/2023 访问量:246
Objectmapper.readerforupdating 不适用于嵌套对象
Objectmapper.readerforupdating not working for nested objects
问:
我在视图的帮助下使用不同的写入权限进行 Objectmapper.readerfordating 时遇到了问题,它在主实体上工作正常,但在嵌套对象上工作不正常。我有以下示例:
public class A {
@JsonView(value={WritePermission.Admin})
private String name;
@JsonView(value={WritePermission.User})
private String property;
@JsonView(value={WritePermission.User})
private List<B> list;
}
public class B {
@JsonView(value={WritePermission.Admin})
private String name;
@JsonView(value={WritePermission.User})
private String property;
}
public class WritePermission {
public WritePermission() {
}
public static class Admin extends WritePermission.User {
public Admin() {
}
}
public static class User {
public User() {
}
}
}
对于反序列化,我使用以下内容:objectMapper.readerForUpdating(initialEntityOfAClass).withView(WritePermission.User.class).forType(A.class).readValue(json) 并且也尝试过这个,但我得到的结果相同: ObjectReader objectReader = objectMapper.readerForUpdating(initialEntityOfAClass);
当我想使用用户写入角色反序列化 json 时,我只想覆盖我有权的属性,这适用于类 A 中的属性(在 name 属性中仍然是旧值,因为我无权更新,属性属性已更新)但它不适用于 B 项列表 - 而不是像 A 一样更改 B 对象(名称仍然是旧值,属性是从 json 更新的)为 B 列表创建新对象,并且 name 保持 null,因为作为用户,我无权将值写入 name 属性。如果我在 B 列表中设置@JsonMerge,而不是合并,我会在一个列表中获取旧的(名称已设置但属性未更改的对象)和新创建的对象(属性更改但名称=null 的对象)......谁能帮我?
答:
这里有两个问题。第一个问题是基于如果存在读取方法,则不读取属性值。因此,嵌套对象的反序列化始终从一个值开始,因此首先构造一个新实例。com.fasterxml.jackson.databind.deser.impl.MethodProperty
null
另一个问题是处理已经是列表一部分的集合元素。
要解决第一个问题,请执行以下操作:
- 如果存在读取方法,则创建一个读取属性值的实现,然后使用该值作为反序列化的基础:
com.fasterxml.jackson.databind.deser.SettableBeanProperty
public class DeepUpdatingMethodProperty extends SettableBeanProperty {
private final MethodProperty delegate;
private final Method propertyReader;
public DeepUpdatingMethodProperty(MethodProperty src, Method propertyReader) {
super(src);
this.delegate = src;
this.propertyReader = propertyReader;
}
@Override
public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
return new DeepUpdatingMethodProperty((MethodProperty) delegate.withValueDeserializer(deser), propertyReader);
}
@Override
public SettableBeanProperty withName(PropertyName newName) {
return new DeepUpdatingMethodProperty((MethodProperty) delegate.withName(newName), propertyReader);
}
@Override
public SettableBeanProperty withNullProvider(NullValueProvider nva) {
return new DeepUpdatingMethodProperty((MethodProperty) delegate.withNullProvider(nva), propertyReader);
}
@Override
public AnnotatedMember getMember() {
return delegate.getMember();
}
@Override
public <A extends Annotation> A getAnnotation(Class<A> acls) {
return delegate.getAnnotation(acls);
}
@Override
public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException {
deserializeSetAndReturn(p, ctxt, instance);
}
@Override
public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance)
throws IOException {
if (instance != null && !p.hasToken(JsonToken.VALUE_NULL)) {
Object readValue;
try {
readValue = readValueFromInstance(instance);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
_throwAsIOE(p, e, instance);
return instance;
}
if (readValue != null) {
return deepUpdateDeserializeSetAndReturn(p, ctxt, instance, readValue);
}
}
return delegate.deserializeSetAndReturn(p, ctxt, instance);
}
private Object deepUpdateDeserializeSetAndReturn(JsonParser p, DeserializationContext ctxt, Object instance,
Object readValue) throws IOException, JacksonException, JsonMappingException {
Object value;
if (_valueTypeDeserializer == null) {
value = _valueDeserializer.deserialize(p, ctxt, readValue);
if (value == null) {
if (NullsConstantProvider.isSkipper(_nullProvider)) {
return instance;
}
value = _nullProvider.getNullValue(ctxt);
}
} else {
value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer, readValue);
}
return setAndReturn(instance, value);
}
private Object readValueFromInstance(Object instance)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return propertyReader.invoke(instance);
}
@Override
public void set(Object instance, Object value) throws IOException {
delegate.set(instance, value);
}
@Override
public Object setAndReturn(Object instance, Object value) throws IOException {
return delegate.setAndReturn(instance, value);
}
}
- 如果存在属性读取方法,请使用 a 修改 a 的属性以使用上述实现
com.fasterxml.jackson.databind.deser.BeanDeserializerModifier
com.fasterxml.jackson.databind.deser.BeanDeserializer
public class DeepUpdatingDeserializerModifier extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc,
JsonDeserializer<?> deserializer) {
if(deserializer instanceof BeanDeserializer beanDeserializer) {
enableDeepUpdateForReadableProperties(beanDesc, beanDeserializer);
}
return deserializer;
}
private void enableDeepUpdateForReadableProperties(BeanDescription beanDesc, BeanDeserializer beanDeserializer) {
Iterator<SettableBeanProperty> iter = beanDeserializer.properties();
while(iter.hasNext()) {
SettableBeanProperty property = iter.next();
if (property instanceof MethodProperty methodProperty) {
Method propertyReader = getPropertyReader(methodProperty, beanDesc);
if (propertyReader != null) {
DeepUpdatingMethodProperty adoptedProperty = new DeepUpdatingMethodProperty(methodProperty, propertyReader);
beanDeserializer.replaceProperty(methodProperty, adoptedProperty);
}
}
}
}
private static Method getPropertyReader(MethodProperty src, BeanDescription beanDesc) {
BeanInfo beanInfo;
Class<?> propertyRawClass = beanDesc.getBeanClass();
try {
beanInfo = Introspector.getBeanInfo(propertyRawClass);
} catch (IntrospectionException e) {
throw new IllegalStateException(MessageFormat.format("Could not introspect {0}.", propertyRawClass), e);
}
return Arrays.asList(beanInfo.getPropertyDescriptors()).stream()
.filter(e -> Objects.equals(src.getName(), e.getName())).map(PropertyDescriptor::getReadMethod)
.findFirst().orElse(null);
}
}
请注意,这仅适用于方法属性,而不适用于构造函数属性。
关于第二个问题,我将集合反序列化更改为不同的内容,以便我使用类似的东西来反序列化集合,而不是普通的 json 表示:
{
"someListProperty": {
"collectionModifications": [{
"_type": "removeIf",
"predicate": {
"_type": "hasPropertyWithValue",
"property": "foo",
"valueMatcher": {
"_type": "or",
"matchers": [{
"_type": "equalTo",
"value": "foobar"
}, {
"_type": "startsWith",
"prefix": "foo."
}
]
}
}
}, {
"_type": "add",
"elements": [{
"foo": "bar"
}, {
"foo": "baz"
}
]
}
]
}
}
上面的示例将首先从列表中删除属性等于或以开头的值,然后添加 中指定的值。您可以根据自己的需要实现自己的。但是,我发现在这种情况下使用地图而不是列表更容易,但我的示例中还没有这样的用例。在示例中,可以使用该属性作为映射键。foo
foobar
foo.
elements
com.fasterxml.jackson.databind.deser.std.CollectionDeserializer
name
上一个:如何对字符串的嵌套列表进行排序
下一个:从xml的字段列表中获取对象
评论