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;
}
}
}