My post from Google Groups:
I am using XStream 1.4.7 to handle de/serialisation of POJOs. As the project develops, I will need to add new features and therefore new class fields. I would like old versions of the app to handle these changes gracefully and to ignore unknown tags.
xs = new XStream();
xs.setMode(XStream.NO_REFERENCES);
xs.ignoreUnknownElements();
xs.registerConverter(new JavaBeanConverter(xs.getMapper(),
new TransientRespectingBeanProvider()), XStream.PRIORITY_VERY_LOW);
Yesterday I added a String field to one of my classes and serialised it. An older version of the app was not able to deserialise the xml.
com.thoughtworks.xstream.converters.ConversionException: No field 'materialNrVersion' found in class 'com.company.ProjectConfiguration' : No field 'materialNrVersion' found in class 'com.company.ProjectConfiguration'
---- Debugging information ----
message : No field 'materialNrVersion' found in class 'com.company.ProjectConfiguration'
cause-exception : com.thoughtworks.xstream.converters.reflection.MissingFieldException
cause-message : No field 'materialNrVersion' found in class 'com.company.ProjectConfiguration'
class : com.company.ProjectConfiguration
required-type : com.company.ProjectConfiguration
converter-type : com.thoughtworks.xstream.converters.javabean.JavaBeanConverter
line number : 192
version : 1.4.7
Should I be doing this a different way?
UPDATE If I comment out the registerConverter call, then the unknown fields are ignored. The JavaBeanConverter seems to be affecting the way the ignoreUnknownElements is handled. The TransientRespectingBeanProvider is intended to ignore properties which have a @Transientannotation.
----- END POST
My workaround was to override the JavaBeanConverter and make the following patch:
// New constructor
public MyJavaBeanConverter(Mapper mapper, JavaBeanProvider beanProvider, boolean ignoreUnknown) {
this(mapper, beanProvider, null);
setIgnoreUnknown(ignoreUnknown);
}
Somewhere near Line 142 in unmarshall() ...
if (mapper.shouldSerializeMember(resultType, propertyName)) {
try {
boolean propertyExistsInClass = beanProvider.propertyDefinedInClass(propertyName, resultType);
if (propertyExistsInClass) {
Class type = determineType(reader, result, propertyName);
Object value = context.convertAnother(result, type);
beanProvider.writeProperty(result, propertyName, value);
seenProperties.add(new FastField(resultType, propertyName));
} else {
throw new MissingFieldException(resultType.getName(), propertyName);
}
} catch (MissingFieldException x) {
// *** ignore missing elements? ***
if (ignoreUnknown == false)
throw x;
else {
System.out.println(String.format("Ignoring %s in %s", propertyName, resultType)); //$NON-NLS-1$
}
}
}
Hope the workaround is not too naïve but it works for me.