protected void doMarshal()

in xstream/src/java/com/thoughtworks/xstream/converters/reflection/AbstractReflectionConverter.java [96:266]


    protected void doMarshal(final Object source, final HierarchicalStreamWriter writer,
            final MarshallingContext context) {
        final List<FieldInfo> fields = new ArrayList<>();
        final Map<String, Field> defaultFieldDefinition = new HashMap<>();
        final Class<?> sourceType = source.getClass();

        // Attributes might be preferred to child elements ...
        reflectionProvider.visitSerializableFields(source, new ReflectionProvider.Visitor() {
            final Set<String> writtenAttributes = new HashSet<>();

            @Override
            public void visit(final String fieldName, final Class<?> type, final Class<?> definedIn,
                    final Object value) {
                if (!mapper.shouldSerializeMember(definedIn, fieldName)) {
                    return;
                }
                if (!defaultFieldDefinition.containsKey(fieldName)) {
                    Class<?> lookupType = sourceType;
                    // See XSTR-457 and OmitFieldsTest
                    if (definedIn != sourceType && !mapper.shouldSerializeMember(lookupType, fieldName)) {
                        lookupType = definedIn;
                    }
                    defaultFieldDefinition.put(fieldName, reflectionProvider.getField(lookupType, fieldName));
                }

                final SingleValueConverter converter = mapper.getConverterFromItemType(fieldName, type, definedIn);
                if (converter != null) {
                    final String attribute = mapper.aliasForAttribute(mapper.serializedMember(definedIn, fieldName));
                    if (value != null) {
                        if (writtenAttributes.contains(fieldName)) {
                            final ConversionException exception = new ConversionException(
                                "Cannot write field as attribute for object, attribute name already in use");
                            exception.add("field-name", fieldName);
                            exception.add("object-type", sourceType.getName());
                            throw exception;
                        }
                        final String str = converter.toString(value);
                        if (str != null) {
                            writer.addAttribute(attribute, str);
                        }
                    }
                    writtenAttributes.add(fieldName);
                } else {
                    fields.add(new FieldInfo(fieldName, type, definedIn, value));
                }
            }
        });

        final FieldMarshaller fieldMarshaller = new FieldMarshaller() {
            @Override
            public void writeField(final String fieldName, final String aliasName, final Class<?> fieldType,
                    final Class<?> definedIn, final Object newObj) {
                final Class<?> actualType = newObj != null ? newObj.getClass() : fieldType;
                writer.startNode(aliasName != null ? aliasName : mapper.serializedMember(sourceType, fieldName),
                    actualType);

                if (newObj != null) {
                    final Class<?> defaultType = mapper.defaultImplementationOf(fieldType);
                    if (!actualType.equals(defaultType)) {
                        final String serializedClassName = mapper.serializedClass(actualType);
                        if (!serializedClassName.equals(mapper.serializedClass(defaultType))) {
                            final String attributeName = mapper.aliasForSystemAttribute("class");
                            if (attributeName != null) {
                                writer.addAttribute(attributeName, serializedClassName);
                            }
                        }
                    }

                    final Field defaultField = defaultFieldDefinition.get(fieldName);
                    if (defaultField.getDeclaringClass() != definedIn) {
                        final String attributeName = mapper.aliasForSystemAttribute("defined-in");
                        if (attributeName != null) {
                            writer.addAttribute(attributeName, mapper.serializedClass(definedIn));
                        }
                    }

                    final Field field = reflectionProvider.getField(definedIn, fieldName);
                    marshallField(context, newObj, field);
                }
                writer.endNode();
            }

            @Override
            public void writeItem(final Object item) {
                if (item == null) {
                    final String name = mapper.serializedClass(null);
                    writer.startNode(name, Mapper.Null.class);
                    writer.endNode();
                } else {
                    final String name = mapper.serializedClass(item.getClass());
                    writer.startNode(name, item.getClass());
                    context.convertAnother(item);
                    writer.endNode();
                }
            }
        };

        final Map<String, Set<Mapper.ImplicitCollectionMapping>> hiddenMappers =
                new HashMap<>();
        for (final FieldInfo info : fields) {
            if (info.value != null) {
                final boolean isCollection = info.value instanceof Collection;
                final boolean isMap = info.value instanceof Map;
                final boolean isArray = info.value.getClass().isArray();
                final Field defaultField = defaultFieldDefinition.get(info.fieldName);
                Mapper.ImplicitCollectionMapping mapping = isCollection || isMap || isArray
                    ? mapper.getImplicitCollectionDefForFieldName(defaultField.getDeclaringClass() == info.definedIn
                        ? sourceType
                        : info.definedIn, info.fieldName)
                    : null;
                if (mapping != null) {
                    Set<Mapper.ImplicitCollectionMapping> mappings = hiddenMappers.get(info.fieldName);
                    if (mappings == null) {
                        mappings = new HashSet<>();
                        mappings.add(mapping);
                        hiddenMappers.put(info.fieldName, mappings);
                    } else {
                        if (!mappings.add(mapping)) {
                            mapping = null;
                        }
                    }
                }
                if (mapping != null) {
                    if (context instanceof ReferencingMarshallingContext) {
                        if (info.value != Collections.EMPTY_LIST
                            && info.value != Collections.EMPTY_SET
                            && info.value != Collections.EMPTY_MAP) {
                            final ReferencingMarshallingContext<?> refContext =
                                    (ReferencingMarshallingContext<?>)context;
                            refContext.registerImplicit(info.value);
                        }
                    }
                    final boolean isEntry = isMap && mapping.getKeyFieldName() == null;
                    for (final Iterator<?> iter = isArray
                        ? new ArrayIterator(info.value)
                        : isCollection
                            ? ((Collection<?>)info.value).iterator()
                            : isEntry
                                ? ((Map<?, ?>)info.value).entrySet().iterator()
                                : ((Map<?, ?>)info.value).values().iterator(); iter.hasNext();) {
                        final Object obj = iter.next();
                        final String itemName;
                        final Class<?> itemType;
                        if (obj == null) {
                            itemType = Object.class;
                            itemName = mapper.serializedClass(null);
                        } else if (isEntry) {
                            final String entryName = mapping.getItemFieldName() != null
                                ? mapping.getItemFieldName()
                                : mapper.serializedClass(Map.Entry.class);
                            final Map.Entry<?, ?> entry = (Map.Entry<?, ?>)obj;
                            writer.startNode(entryName, entry.getClass());
                            fieldMarshaller.writeItem(entry.getKey());
                            fieldMarshaller.writeItem(entry.getValue());
                            writer.endNode();
                            continue;
                        } else if (mapping.getItemFieldName() != null) {
                            itemType = mapping.getItemType();
                            itemName = mapping.getItemFieldName();
                        } else {
                            itemType = obj.getClass();
                            itemName = mapper.serializedClass(itemType);
                        }
                        fieldMarshaller.writeField(info.fieldName, itemName, itemType, info.definedIn, obj);
                    }
                } else {
                    fieldMarshaller.writeField(info.fieldName, null, info.type, info.definedIn, info.value);
                }
            }
        }
    }