private static void InnerSerialize(object obj, StringBuilder sb, string keyPrefix = null)
        {
            if (obj == null)
            {
                sb.AppendLine($"{keyPrefix ?? "value"} = ");
                return;
            }

            var type = obj.GetType();

            if (TypeIsSimple(type))
            {
                sb.AppendLine($"{keyPrefix ?? "value"} = " + obj);
            }
            else if (IsSimpleDictionary(type))
            {
                foreach (var(key, value) in DictionaryInspector.EnumerateSimpleDictionary(obj))
                {
                    InnerSerialize(value, sb, keyPrefix == null ? key : $"{keyPrefix}.{key}");
                }
            }
            else if (obj is IEnumerable)
            {
                InnerSerialize(null, sb, keyPrefix);
            }
            else if (HasCustomToString(type))
            {
                InnerSerialize(obj.ToString(), sb, keyPrefix);
            }
            else if (HasPublicProperties(type))
            {
                foreach (var(key, value) in ObjectPropertiesExtractor.ExtractProperties(obj))
                {
                    InnerSerialize(value, sb, keyPrefix == null ? key : $"{keyPrefix}.{key}");
                }
            }
            else
            {
                sb.AppendLine($"{keyPrefix ?? "value"} = " + obj);
            }
        }
        private static IPrintToken CreateInternal([CanBeNull] object item, [NotNull] HashSet <object> path, [NotNull] PrintSettings settings)
        {
            if (item == null)
            {
                return(NullValue);
            }

            if (!path.Add(item))
            {
                return(CyclicValue);
            }

            try
            {
                var itemType = item.GetType();

                using (SecurityHelper.StartSecurityScope(itemType))
                {
                    if (ToStringDetector.HasCustomToString(itemType))
                    {
                        return(new ValueToken(item.ToString()));
                    }

                    if (CustomFormatters.TryFormat(item, out var customFormatting))
                    {
                        return(new ValueToken(customFormatting));
                    }

                    if (DictionaryInspector.IsSimpleDictionary(itemType))
                    {
                        var pairs  = DictionaryInspector.EnumerateSimpleDictionary(item);
                        var tokens = pairs.Select(pair => new PropertyToken(pair.Item1, CreateInternal(pair.Item2, path, settings))).ToArray();
                        if (tokens.Length == 0)
                        {
                            return(EmptyObjectValue);
                        }

                        return(new ObjectToken(tokens));
                    }

                    if (item is IEnumerable sequence)
                    {
                        if (!sequence.GetEnumerator().MoveNext())
                        {
                            return(EmptySequenceValue);
                        }

                        var tokens = new List <IPrintToken>();

                        foreach (var element in sequence)
                        {
                            tokens.Add(CreateInternal(element, path, settings));
                        }

                        return(new SequenceToken(tokens));
                    }

                    var fieldsAndProperties = new List <PropertyToken>();

                    foreach (var field in itemType.GetInstanceFields())
                    {
                        fieldsAndProperties.Add(ConstructProperty(field, () => CreateInternal(field.GetValue(item), path, settings), settings));
                    }

                    foreach (var property in itemType.GetInstanceProperties())
                    {
                        fieldsAndProperties.Add(ConstructProperty(property, () => CreateInternal(property.GetValue(item), path, settings), settings));
                    }

                    if (fieldsAndProperties.Count == 0)
                    {
                        return(EmptyObjectValue);
                    }

                    return(new ObjectToken(fieldsAndProperties));
                }
            }
            finally
            {
                path.Remove(item);
            }
        }
        private static ISettingsNode ParseObject([CanBeNull] string name, [CanBeNull] object item, [NotNull] HashSet <object> path, [NotNull] ObjectSourceSettings settings)
        {
            if (item == null)
            {
                return(new ValueNode(name, null));
            }

            if (!path.Add(item))
            {
                throw new ArgumentException("Object has cyclic dependency.");
            }

            try
            {
                var itemType = item.GetType();

                if (CustomFormatters.TryFormat(item, out var customFormatting))
                {
                    return(new ValueNode(name, customFormatting));
                }

                if (ParseMethodFinder.HasAnyKindOfParseMethod(itemType) && ToStringDetector.TryInvokeCustomToString(itemType, item, out var asString))
                {
                    return(new ValueNode(name, asString));
                }

                if (DictionaryInspector.IsSimpleDictionary(itemType))
                {
                    return(ParseDictionary(name, DictionaryInspector.EnumerateSimpleDictionary(item), path, settings));
                }

                if (item is IEnumerable sequence)
                {
                    return(ParseEnumerable(name, sequence, path, settings));
                }

                var fieldsAndProperties = new List <ISettingsNode>();

                foreach (var field in itemType.GetInstanceFields())
                {
                    var fieldValue = field.GetValue(item);
                    if (fieldValue != null || !settings.IgnoreFieldsWithNullValue)
                    {
                        fieldsAndProperties.Add(ParseObject(field.Name, fieldValue, path, settings));
                    }
                }

                foreach (var property in itemType.GetInstanceProperties())
                {
                    var propertyValue = property.GetValue(item);
                    if (propertyValue != null || !settings.IgnoreFieldsWithNullValue)
                    {
                        fieldsAndProperties.Add(ParseObject(property.Name, propertyValue, path, settings));
                    }
                }

                return(new ObjectNode(name, fieldsAndProperties));
            }
            finally
            {
                path.Remove(item);
            }
        }
 private static bool IsSimpleDictionary(Type type) =>
 DictionaryInspector.IsSimpleDictionary(type);