简体   繁体   中英

Deserializing transient fields with XStream 1.4.2

I've faced with a requirement to deserialize fields that possibly can be transient using XStream 1.4.2. Despite of that, such fields may be annotated with both @XStreamAlias and @XStreamAsAttribute . Yes, I know, it sounds weird, and this is an indicator of bad design, but this is what I currently have. Since XStream offers a way to specify custom converter, I tried to extend com.thoughtworks.xstream.converters.reflection.ReflectionConverter in order to override the default way of omitting all transient fields trying to make XStream allow to deserialize them. However, I've fully stuck having two ideas to implement such a converter, but none of them works. So here is what I tried:

The 1st way doesn't work:

public final class TransientSimpleConverter extends ReflectionConverter {

    private final Class<?> type;

    private TransientSimpleConverter(Class<?> type, Mapper mapper, ReflectionProvider reflectionProvider) {
        super(mapper, reflectionProvider);
        this.type = type;
    }

    public static TransientSimpleConverter transientSimpleConverter(Class<?> type, XStream xStream) {
        return new TransientSimpleConverter(type, xStream.getMapper(), xStream.getReflectionProvider());
    }

    @Override
    protected boolean shouldUnmarshalTransientFields() {
        return true;
    }

    @Override
    public boolean canConvert(Class type) {
        return this.type == type;
    }

}

The 2nd way doesn't work either:

public final class TransientComplexConverter extends ReflectionConverter {

    private final Class<?> type;

    private TransientComplexConverter(Class<?> type, Mapper mapper, ReflectionProvider provider) {
        super(mapper, provider);
        this.type = type;
    }

    public static TransientComplexConverter transientComplexConverter(Class<?> type, Mapper mapper, Iterable<String> fieldNames) {
        return new TransientComplexConverter(type, mapper, TransientHackReflectionProvider.transientHackReflectionProvider(type, fieldNames));
    }

    @Override
    public boolean canConvert(Class type) {
        return this.type == type;
    }

    private static final class TransientHackReflectionProvider extends PureJavaReflectionProvider {

        private final Class<?> type;
        private final Collection<Field> allowedFields;
        private final Collection<String> allowedAliases;

        private TransientHackReflectionProvider(Class<?> type, Collection<Field> allowedFields, Collection<String> allowedAliases) {
            this.type = type;
            this.allowedFields = allowedFields;
            this.allowedAliases = allowedAliases;
        }

        public static TransientHackReflectionProvider transientHackReflectionProvider(final Class<?> type, Iterable<String> fieldNames) {
            final Collection<Field> allowedFields = from(fieldNames).transform(new Function<String, Field>() {
                @Override
                public Field apply(String name) {
                    return field(type, name);
                }
            }).toList();
            final Collection<String> allowedAliases = transform(allowedFields, new Function<Field, String>() {
                @Override
                public String apply(Field f) {
                    return f.getName();
                }
            });
            return new TransientHackReflectionProvider(type, allowedFields, allowedAliases);
        }

        @Override
        protected boolean fieldModifiersSupported(Field field) {
            return allowedFields.contains(field) ? true : super.fieldModifiersSupported(field);
        }

        @Override
        public boolean fieldDefinedInClass(String fieldName, Class type) {
            return type == this.type && allowedAliases.contains(fieldName) ? true : super.fieldDefinedInClass(fieldName, type);
        }

        private static final Field field(Class<?> type, String name) {
            try {
                final Field field = type.getDeclaredField(name);
                checkArgument(isTransient(field.getModifiers()), name + " is not transient");
                checkArgument(field.getAnnotation(XStreamAsAttribute.class) != null, name + " must be annotated with XStreamAsAttribute");
                checkArgument(field.getAnnotation(XStreamAlias.class) != null, name + " must be annotated with XStreamAlias");
                return field;
            } catch (final SecurityException ex) {
                throw new RuntimeException(ex);
            } catch (final NoSuchFieldException ex) {
                throw new RuntimeException(ex);
            }
        }

    }

}

Any suggestions or ideas for a workaround? Thanks in advance.

I know this post is old, but maybe someone is still interested. My solution:

XStream xstream = new XStream(new MyPureJavaReflectionProvider());

class MyPureJavaReflectionProvider extends PureJavaReflectionProvider {

    public MyPureJavaReflectionProvider() {
        this(new FieldDictionary(new ImmutableFieldKeySorter()));
    }

    public MyPureJavaReflectionProvider(FieldDictionary fieldDictionary) {
        super(fieldDictionary);
    }

    protected boolean fieldModifiersSupported(Field field) {
        int modifiers = field.getModifiers();
        return !Modifier.isStatic(modifiers);
    }

    public boolean fieldDefinedInClass(String fieldName, Class type) {
        Field field = fieldDictionary.fieldOrNull(type, fieldName, null);
        return field != null && fieldModifiersSupported(field);
    }   

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM