#nullable enable 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(Boolean format, String? typeSpecifier, String[]? includeProperties, String[]? excludeProperties = null, Boolean includeNonPublic = true, IReadOnlyCollection? parentReferences = null, JsonSerializerCase jsonSerializerCase = JsonSerializerCase.None) { this._includeProperties = includeProperties; this._excludeProperties = excludeProperties; this.IncludeNonPublic = includeNonPublic; this.Format = format; this.TypeSpecifier = typeSpecifier; this.JsonSerializerCase = jsonSerializerCase; if(parentReferences == null) { return; } foreach(WeakReference parentReference in parentReferences.Where(x => x.IsAlive)) { _ = this.IsObjectPresent(parentReference.Target); } } /// /// Gets a value indicating whether this is format. /// /// /// true if format; otherwise, false. /// public Boolean 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 Boolean IncludeNonPublic { get; } /// /// Gets the json serializer case. /// /// /// The json serializer case. /// public JsonSerializerCase JsonSerializerCase { get; } internal Boolean IsObjectPresent(Object? target) { if(target == null) { return false; } Int32 hashCode = target.GetHashCode(); if(this._parentReferences.ContainsKey(hashCode)) { if(this._parentReferences[hashCode].Any(p => ReferenceEquals(p.Target, target))) { return true; } this._parentReferences[hashCode].Add(new WeakReference(target)); return false; } this._parentReferences.Add(hashCode, new List { new WeakReference(target) }); return false; } internal Dictionary GetProperties(Type targetType) => this.GetPropertiesCache(targetType).When(() => this._includeProperties?.Length > 0, query => query.Where(p => this._includeProperties.Contains(p.Key.Item1))).When(() => this._excludeProperties?.Length > 0, query => query.Where(p => !this._excludeProperties.Contains(p.Key.Item1))).ToDictionary(x => x.Key.Item2, x => x.Value); private Dictionary, MemberInfo> GetPropertiesCache(Type targetType) { if(TypeCache.TryGetValue(targetType, out Dictionary, MemberInfo>? current)) { return current; } List 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)); } Dictionary, MemberInfo> value = fields.ToDictionary(x => Tuple.Create(x.Name, x.GetCustomAttribute()?.PropertyName ?? x.Name.GetNameWithCase(this.JsonSerializerCase)), x => x); _ = TypeCache.TryAdd(targetType, value); return value; } } }