java Objectmapper.readerforupdating对嵌套对象不起作用

xzv2uavs  于 2023-05-05  发布在  Java
关注(0)|答案(1)|浏览(201)

我有一个问题与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);
当我想反序列化一个具有User write角色的json时,我想只覆盖我有权限的属性,这对于类A的属性很好用(在name属性中仍然是旧值,因为我没有更新的权限,property属性已更新)但它不适用于B项列表-而不是像A一样更改B对象(name保持旧值,property从json更新)为B的列表创建新对象,name保持null,因为作为用户,我没有权限将值写入name属性。如果我在B列表上设置@JsonMerge,而不是合并,我会在一个列表中获得旧的(已经设置了名称但属性未更改的对象)和新创建的对象(属性更改但名称为null的对象)。有人能帮帮我吗?

j5fpnvbx

j5fpnvbx1#

这里有两个问题。第一个问题是com.fasterxml.jackson.databind.deser.impl.MethodProperty在存在read方法时不阅读属性值。所以嵌套对象的反序列化总是从null值开始,因此首先构造一个新示例。
另一个问题是处理已经是列表一部分的集合元素。
要解决第一个问题,请执行以下操作:

  • 创建一个com.fasterxml.jackson.databind.deser.SettableBeanProperty实现,如果存在read方法,它将读取属性值,然后使用该值作为反序列化的基础:
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);
    }
}

请注意,这只适用于方法属性,而不适用于构造函数属性。
关于第二个问题,我将集合的反序列化更改为不同的东西,以便代替普通的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。然而,我发现在这种情况下使用Map而不是列表更容易,但我的示例中还没有这样的用例。在您的示例中,您可以使用name属性作为Map键。

相关问题