using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using Swan.Reflection; namespace Swan.Formatters { /// /// A very simple, light-weight JSON library written by Mario /// to teach Geo how things are done /// /// This is an useful helper for small tasks but it doesn't represent a full-featured /// serializer such as the beloved Json.NET. /// public class SerializerOptions { private static readonly ConcurrentDictionary, MemberInfo>> TypeCache = new ConcurrentDictionary, MemberInfo>>(); private readonly string[]? _includeProperties; private readonly string[]? _excludeProperties; private readonly Dictionary> _parentReferences = new Dictionary>(); /// /// Initializes a new instance of the class. /// /// if set to true [format]. /// The type specifier. /// The include properties. /// The exclude properties. /// if set to true [include non public]. /// The parent references. /// The json serializer case. public SerializerOptions( bool format, string? typeSpecifier, string[]? includeProperties, string[]? excludeProperties = null, bool includeNonPublic = true, IReadOnlyCollection? parentReferences = null, JsonSerializerCase jsonSerializerCase = JsonSerializerCase.None) { _includeProperties = includeProperties; _excludeProperties = excludeProperties; IncludeNonPublic = includeNonPublic; Format = format; TypeSpecifier = typeSpecifier; JsonSerializerCase = jsonSerializerCase; if (parentReferences == null) return; foreach (var parentReference in parentReferences.Where(x => x.IsAlive)) { IsObjectPresent(parentReference.Target); } } /// /// Gets a value indicating whether this is format. /// /// /// true if format; otherwise, false. /// public bool Format { get; } /// /// Gets the type specifier. /// /// /// The type specifier. /// public string? TypeSpecifier { get; } /// /// Gets a value indicating whether [include non public]. /// /// /// true if [include non public]; otherwise, false. /// public bool IncludeNonPublic { get; } /// /// Gets the json serializer case. /// /// /// The json serializer case. /// public JsonSerializerCase JsonSerializerCase { get; } internal bool IsObjectPresent(object target) { var hashCode = target.GetHashCode(); if (_parentReferences.ContainsKey(hashCode)) { if (_parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target))) return true; _parentReferences[hashCode].Add(new WeakReference(target)); return false; } _parentReferences.Add(hashCode, new List { new WeakReference(target) }); return false; } internal Dictionary GetProperties(Type targetType) => GetPropertiesCache(targetType) .When(() => _includeProperties?.Length > 0, query => query.Where(p => _includeProperties.Contains(p.Key.Item1))) .When(() => _excludeProperties?.Length > 0, query => query.Where(p => !_excludeProperties.Contains(p.Key.Item1))) .ToDictionary(x => x.Key.Item2, x => x.Value); private Dictionary, MemberInfo> GetPropertiesCache(Type targetType) { if (TypeCache.TryGetValue(targetType, out var current)) return current; var fields = new List(PropertyTypeCache.DefaultCache.Value.RetrieveAllProperties(targetType).Where(p => p.CanRead)); // If the target is a struct (value type) navigate the fields. if (targetType.IsValueType) { fields.AddRange(FieldTypeCache.DefaultCache.Value.RetrieveAllFields(targetType)); } var value = fields .ToDictionary( x => Tuple.Create(x.Name, x.GetCustomAttribute()?.PropertyName ?? x.Name.GetNameWithCase(JsonSerializerCase)), x => x); TypeCache.TryAdd(targetType, value); return value; } } }