我在 Objectmapper.readerforupdating 有问题,在视图的帮助下使用不同的写权限,它在主实体上工作正常,但在嵌套对象上工作不正常。我有以下例子:
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 的属性(在名称属性中仍然是旧值,因为我没有更新权,属性属性已更新)但它不适用于 B 项目列表 - 而不是像 A 一样更改 B 对象(名称保持旧值,属性从 json 更新)为列表创建新对象B 和 name 保持为空,因为作为用户我没有权限将值写入 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);
}
}
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);
}
}
请注意,这仅适用于方法属性,不适用于构造函数属性。
您似乎正在尝试使用 Jackson 的
ObjectMapper
和 JsonView
来更新 A 类的实例,以限制可以根据用户的写权限更新的字段。但是,您在更新 A 类列表中 B 类的嵌套对象时遇到问题。
解决这个问题的一种方法是使用 A 类的自定义反序列化器,它在更新 B 类的嵌套对象时考虑到用户的写权限。这是 A 类的自定义反序列化器的示例:
public class ADeserializer extends JsonDeserializer<A> {
@Override
public A deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = jp.getCodec().readTree(jp);
A a = (A) ctxt.getAttribute("initialEntityOfAClass");
if (a == null) {
throw new IllegalStateException("initialEntityOfAClass attribute not found in DeserializationContext");
}
Iterator<Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Entry<String, JsonNode> field = fields.next();
String fieldName = field.getKey();
if (fieldName.equals("name") && !mapper.isEnabled(MapperFeature.USE_ANNOTATIONS)) {
// Do not update name field if annotations are not enabled
continue;
}
if (fieldName.equals("list")) {
// Update list of B objects
JsonNode listNode = field.getValue();
List<B> list = new ArrayList<>();
for (JsonNode item : listNode) {
B b = mapper.readerForUpdating(a.new B()).withView(WritePermission.User.class).readValue(item);
list.add(b);
}
a.setList(list);
} else {
// Update field of class A
mapper.readerForUpdating(a).withView(WritePermission.User.class).with(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE).withAttribute("initialEntityOfAClass", a).readValue(field.getValue().traverse());
}
}
return a;
}
}
这个自定义反序列化器使用
ObjectMapper.readerForUpdating()
方法根据用户的写入权限更新类A的字段。对于 B 对象列表,它使用 mapper.readerForUpdating(a.new B())
创建 B 类的新实例,以确保在更新 B 类的字段时考虑写入权限。
要使用此自定义反序列化器,您可以使用
ObjectMapper
类将其注册到 SimpleModule
:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(A.class, new ADeserializer());
mapper.registerModule(module);
A initialEntityOfAClass = new A();
String json = "{\"name\":\"old name\",\"property\":\"new property\",\"list\":[{\"name\":\"old name\",\"property\":\"new property\"}]}";
A a = mapper.readerForUpdating(initialEntityOfAClass).withView(WritePermission.User.class).readValue(json);
With this custom deserializer, you should be able to update the nested objects of class B in the list of class A based on the write permission of the user.