using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using Unosquare.Swan.Attributes;
namespace Unosquare.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 partial class Json {
private 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>();
public SerializerOptions(
Boolean format,
String typeSpecifier,
String[] includeProperties,
String[] excludeProperties = null,
Boolean includeNonPublic = true,
IReadOnlyCollection parentReferences = null) {
this._includeProperties = includeProperties;
this._excludeProperties = excludeProperties;
this.IncludeNonPublic = includeNonPublic;
this.Format = format;
this.TypeSpecifier = typeSpecifier;
if(parentReferences == null) {
return;
}
foreach(WeakReference parentReference in parentReferences.Where(x => x.IsAlive)) {
_ = this.IsObjectPresent(parentReference.Target);
}
}
public Boolean Format {
get;
}
public String TypeSpecifier {
get;
}
public Boolean IncludeNonPublic {
get;
}
internal Boolean IsObjectPresent(Object target) {
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)
=> 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 static Dictionary, MemberInfo> GetPropertiesCache(Type targetType) {
if(TypeCache.TryGetValue(targetType, out Dictionary, MemberInfo> current)) {
return current;
}
List fields =
new List(PropertyTypeCache.RetrieveAllProperties(targetType).Where(p => p.CanRead));
// If the target is a struct (value type) navigate the fields.
if(targetType.IsValueType()) {
fields.AddRange(FieldTypeCache.RetrieveAllFields(targetType));
}
Dictionary, MemberInfo> value = fields
.ToDictionary(
x => Tuple.Create(x.Name,
x.GetCustomAttribute()?.PropertyName ?? x.Name),
x => x);
_ = TypeCache.TryAdd(targetType, value);
return value;
}
}
}
}