public void doMarshal()

in xstream/src/java/com/thoughtworks/xstream/converters/reflection/SerializableConverter.java [126:294]


    public void doMarshal(final Object source, final HierarchicalStreamWriter writer,
            final MarshallingContext context) {
        final String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_SERIALIZATION);
        if (attributeName != null) {
            writer.addAttribute(attributeName, ATTRIBUTE_VALUE_CUSTOM);
        }

        // this is an array as it's a non final value that's accessed from an anonymous inner class.
        final Class<?>[] currentTypeRef = new Class<?>[1];
        final boolean[] writtenClassWrapper = {false};

        final CustomObjectOutputStream.StreamCallback callback = new CustomObjectOutputStream.StreamCallback() {

            @Override
            public void writeToStream(final Object object) {
                if (object == null) {
                    writer.startNode(ELEMENT_NULL);
                    writer.endNode();
                } else {
                    writer.startNode(mapper.serializedClass(object.getClass()), object.getClass());
                    context.convertAnother(object);
                    writer.endNode();
                }
            }

            @Override
            public void writeFieldsToStream(final Map<String, Object> fields) {
                final Class<?> currentType = currentTypeRef[0];
                final ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(currentType);

                writer.startNode(ELEMENT_DEFAULT);
                for (final String name : fields.keySet()) {
                    if (!mapper.shouldSerializeMember(currentType, name)) {
                        continue;
                    }
                    final ObjectStreamField field = objectStreamClass.getField(name);
                    final Object value = fields.get(name);
                    if (field == null) {
                        throw new MissingFieldException(value.getClass().getName(), name);
                    }
                    if (value != null) {
                        writer.startNode(mapper.serializedMember(source.getClass(), name), value.getClass());
                        if (field.getType() != value.getClass() && !field.getType().isPrimitive()) {
                            final String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS);
                            if (attributeName != null) {
                                writer.addAttribute(attributeName, mapper.serializedClass(value.getClass()));
                            }
                        }
                        context.convertAnother(value);
                        writer.endNode();
                    }
                }
                writer.endNode();
            }

            @Override
            public void defaultWriteObject() {
                boolean writtenDefaultFields = false;

                final Class<?> currentType = currentTypeRef[0];
                final ObjectStreamClass objectStreamClass = ObjectStreamClass.lookup(currentType);
                if (objectStreamClass == null) {
                    return;
                }

                for (final ObjectStreamField field : objectStreamClass.getFields()) {
                    final Object value = readField(field, currentType, source);
                    if (value != null) {
                        if (!writtenClassWrapper[0]) {
                            writer.startNode(mapper.serializedClass(currentType));
                            writtenClassWrapper[0] = true;
                        }
                        if (!writtenDefaultFields) {
                            writer.startNode(ELEMENT_DEFAULT);
                            writtenDefaultFields = true;
                        }
                        if (!mapper.shouldSerializeMember(currentType, field.getName())) {
                            continue;
                        }

                        final Class<?> actualType = value.getClass();
                        writer.startNode(mapper.serializedMember(source.getClass(), field.getName()), actualType);
                        final Class<?> defaultType = mapper.defaultImplementationOf(field.getType());
                        if (!actualType.equals(defaultType)) {
                            final String attributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS);
                            if (attributeName != null) {
                                writer.addAttribute(attributeName, mapper.serializedClass(actualType));
                            }
                        }

                        context.convertAnother(value);

                        writer.endNode();
                    }
                }
                if (writtenClassWrapper[0] && !writtenDefaultFields) {
                    writer.startNode(ELEMENT_DEFAULT);
                    writer.endNode();
                } else if (writtenDefaultFields) {
                    writer.endNode();
                }
            }

            @Override
            public void flush() {
                writer.flush();
            }

            @Override
            public void close() {
                throw new UnsupportedOperationException(
                    "Objects are not allowed to call ObjectOutputStream.close() from writeObject()");
            }
        };

        try {
            boolean mustHandleUnserializableParent = false;
            for (final Class<?> currentType : hierarchyFor(source.getClass())) {
                currentTypeRef[0] = currentType;
                if (!Serializable.class.isAssignableFrom(currentType)) {
                    mustHandleUnserializableParent = true;
                    continue;
                } else {
                    if (mustHandleUnserializableParent) {
                        marshalUnserializableParent(writer, context, source);
                        mustHandleUnserializableParent = false;
                    }
                    if (serializationMembers.supportsWriteObject(currentType, false)) {
                        writtenClassWrapper[0] = true;
                        writer.startNode(mapper.serializedClass(currentType));
                        if (currentType != mapper.defaultImplementationOf(currentType)) {
                            final String classAttributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS);
                            if (classAttributeName != null) {
                                writer.addAttribute(classAttributeName, currentType.getName());
                            }
                        }
                        @SuppressWarnings("resource")
                        final CustomObjectOutputStream objectOutputStream = CustomObjectOutputStream.getInstance(
                            context, callback);
                        serializationMembers.callWriteObject(currentType, source, objectOutputStream);
                        objectOutputStream.popCallback();
                        writer.endNode();
                    } else if (serializationMembers.supportsReadObject(currentType, false)) {
                        // Special case for objects that have readObject(), but not writeObject().
                        // The class wrapper is always written, whether or not this class in the hierarchy has
                        // serializable fields. This guarantees that readObject() will be called upon deserialization.
                        writtenClassWrapper[0] = true;
                        writer.startNode(mapper.serializedClass(currentType));
                        if (currentType != mapper.defaultImplementationOf(currentType)) {
                            final String classAttributeName = mapper.aliasForSystemAttribute(ATTRIBUTE_CLASS);
                            if (classAttributeName != null) {
                                writer.addAttribute(classAttributeName, currentType.getName());
                            }
                        }
                        callback.defaultWriteObject();
                        writer.endNode();
                    } else {
                        writtenClassWrapper[0] = false;
                        callback.defaultWriteObject();
                        if (writtenClassWrapper[0]) {
                            writer.endNode();
                        }
                    }
                }
            }
        } catch (final IOException e) {
            throw new StreamException("Cannot write defaults", e);
        }
    }