Search code examples
xmlmarshallingunmarshallingxstream

Cannot apply @XStreamConverter ToAttributedValueConverter


I want some XML tree classes to unmarshall as text nodes, but couldn't get the thing to work in my use case : an abstract class hierarchy.

I tried to register the converter programmatically, to declare it over concrete classes (API doc even says that inherited fields are handled).

@Bean
public Marshaller marshaller() {
    XStreamMarshaller marshaller = new XStreamMarshaller();
    marshaller.setAutodetectAnnotations(true);
    return marshaller;
}
@XStreamConverter(value = ToAttributedValueConverter.class, strings = "value")
public abstract class AbstractProp {
    @XStreamAsAttribute
    private final String name;
    private final String value;
}
@XStreamAlias("boolProp")
public class BoolProp extends AbstractProp {
    public BoolProp(String name, Boolean value) {
        super(name, value.toString());
    }
}

EXPECTED <boolProp name="foobar">false</boolProp>

ACTUAL <boolProp name="foobar"><value>false</value></boolProp>


Solution

  • Ok. I found in the source code what's wrong with inheritance:

    public class ToAttributedValueConverter implements Converter {
    ...
    @Override
    public boolean canConvert(final Class<?> type) {
        return this.type == type;
    }
    

    So I made this as a workaround:

    public class TextNodeConverter extends ToAttributedValueConverter {
    
    private final Class<?> type;
    
    public TextNodeConverter(Class type, Mapper mapper, ReflectionProvider reflectionProvider, ConverterLookup lookup) {
        this(type, mapper, reflectionProvider, lookup, null, null);
    }
    
    public TextNodeConverter(Class type, Mapper mapper, ReflectionProvider reflectionProvider, ConverterLookup lookup,
            String valueFieldName) {
        this(type, mapper, reflectionProvider, lookup, valueFieldName, null);
    }
    
    public TextNodeConverter(Class type, Mapper mapper, ReflectionProvider reflectionProvider, ConverterLookup lookup,
            String valueFieldName, Class valueDefinedIn) {
        super(type, mapper, reflectionProvider, lookup, valueFieldName, valueDefinedIn);
        this.type = type;
    }
    
    @Override
    public boolean canConvert(final Class type) {
        return this.type.isAssignableFrom(type);
    }
    }
    

    And, well... after commuting the converter, that worked like a charm :-)