in xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java [322:484]
public Object doUnmarshal(final Object result, final HierarchicalStreamReader reader,
final UnmarshallingContext context) {
// this is an array as it's a non final value that's accessed from an anonymous inner class.
final Class<?>[] currentType = new Class<?>[1];
final String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_SERIALIZATION);
if (attributeName != null && !ATTRIBUTE_VALUE_CUSTOM.equals(reader.getAttribute(attributeName))) {
throw new ConversionException("Cannot deserialize object with new readObject()/writeObject() methods");
}
final CustomObjectInputStream.StreamCallback callback = new CustomObjectInputStream.StreamCallback() {
@Override
public Object readFromStream() {
reader.moveDown();
final Class<?> type = HierarchicalStreams.readClassType(reader, mapper);
final Object value = context.convertAnother(result, type);
reader.moveUp();
return value;
}
@Override
public Map<String, Object> readFieldsFromStream() {
final Map<String, Object> fields = new HashMap<>();
reader.moveDown();
if (reader.getNodeName().equals(ELEMENT_FIELDS)) {
// Maintain compatibility with XStream 1.1.0
while (reader.hasMoreChildren()) {
reader.moveDown();
if (!reader.getNodeName().equals(ELEMENT_FIELD)) {
throw new ConversionException("Expected <"
+ ELEMENT_FIELD
+ "/> element inside <"
+ ELEMENT_FIELD
+ "/>");
}
final String name = reader.getAttribute(ATTRIBUTE_NAME);
final Class<?> type = mapper.realClass(reader.getAttribute(ATTRIBUTE_CLASS));
final Object value = context.convertAnother(result, type);
fields.put(name, value);
reader.moveUp();
}
} else if (reader.getNodeName().equals(ELEMENT_DEFAULT)) {
// New format introduced in XStream 1.1.1
final ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(currentType[0]);
while (reader.hasMoreChildren()) {
reader.moveDown();
final String name = mapper.realMember(currentType[0], reader.getNodeName());
if (mapper.shouldSerializeMember(currentType[0], name)) {
final String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper);
Class<?> type;
if (classAttribute != null) {
type = mapper.realClass(classAttribute);
} else {
final ObjectStreamField field = objectStreamClass.getField(name);
if (field == null) {
throw new MissingFieldException(currentType[0].getName(), name);
}
type = field.getType();
}
final Object value = context.convertAnother(result, type);
fields.put(name, value);
}
reader.moveUp();
}
} else {
throw new ConversionException("Expected <"
+ ELEMENT_FIELDS
+ "/> or <"
+ ELEMENT_DEFAULT
+ "/> element when calling ObjectInputStream.readFields()");
}
reader.moveUp();
return fields;
}
@Override
public void defaultReadObject() {
if (serializationMembers.getSerializablePersistentFields(currentType[0]) != null) {
readFieldsFromStream();
return;
}
if (!reader.hasMoreChildren()) {
return;
}
reader.moveDown();
if (!reader.getNodeName().equals(ELEMENT_DEFAULT)) {
throw new ConversionException("Expected <" + ELEMENT_DEFAULT + "/> element in readObject() stream");
}
while (reader.hasMoreChildren()) {
reader.moveDown();
final String fieldName = mapper.realMember(currentType[0], reader.getNodeName());
if (mapper.shouldSerializeMember(currentType[0], fieldName)) {
final String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper);
final Class<?> type;
if (classAttribute != null) {
type = mapper.realClass(classAttribute);
} else {
type = mapper.defaultImplementationOf(reflectionProvider.getFieldType(result, fieldName,
currentType[0]));
}
final Object value = context.convertAnother(result, type);
reflectionProvider.writeField(result, fieldName, value, currentType[0]);
}
reader.moveUp();
}
reader.moveUp();
}
@Override
public void registerValidation(final ObjectInputValidation validation, final int priority) {
context.addCompletionCallback(new Runnable() {
@Override
public void run() {
try {
validation.validateObject();
} catch (final InvalidObjectException e) {
throw new ObjectAccessException("Cannot validate object", e);
}
}
}, priority);
}
@Override
public void close() {
throw new UnsupportedOperationException(
"Objects are not allowed to call ObjectInputStream.close() from readObject()");
}
};
while (reader.hasMoreChildren()) {
reader.moveDown();
final String nodeName = reader.getNodeName();
if (nodeName.equals(ELEMENT_UNSERIALIZABLE_PARENTS)) {
super.doUnmarshal(result, reader, context);
} else {
final String classAttribute = HierarchicalStreams.readClassAttribute(reader, mapper);
if (classAttribute == null) {
currentType[0] = mapper.defaultImplementationOf(mapper.realClass(nodeName));
} else {
currentType[0] = mapper.realClass(classAttribute);
}
if (serializationMembers.supportsReadObject(currentType[0], false)) {
@SuppressWarnings("resource")
final CustomObjectInputStream objectInputStream = CustomObjectInputStream.getInstance(context,
callback, classLoaderReference);
serializationMembers.callReadObject(currentType[0], result, objectInputStream);
objectInputStream.popCallback();
} else {
try {
callback.defaultReadObject();
} catch (final IOException e) {
throw new StreamException("Cannot read defaults", e);
}
}
}
reader.moveUp();
}
return result;
}