namespace Unosquare.Swan.Formatters
{
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Attributes;
///
/// 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 static partial class Json
{
private class Converter
{
private static readonly ConcurrentDictionary MemberInfoNameCache =
new ConcurrentDictionary();
private static readonly ConcurrentDictionary ListAddMethodCache = new ConcurrentDictionary();
private readonly object _target;
private readonly Type _targetType;
private readonly bool _includeNonPublic;
private Converter(
object source,
Type targetType,
ref object targetInstance,
bool includeNonPublic)
{
_targetType = targetInstance != null ? targetInstance.GetType() : targetType;
_includeNonPublic = includeNonPublic;
if (source == null)
{
return;
}
var sourceType = source.GetType();
if (_targetType == null || _targetType == typeof(object)) _targetType = sourceType;
if (sourceType == _targetType)
{
_target = source;
return;
}
if (!TrySetInstance(targetInstance, source, ref _target))
return;
ResolveObject(source, ref _target);
}
///
/// Converts a json deserialized object (simple type, dictionary or list) to a new instance of the specified target type.
///
/// The source.
/// Type of the target.
/// if set to true [include non public].
/// The target object.
internal static object FromJsonResult(object source,
Type targetType,
bool includeNonPublic)
{
object nullRef = null;
return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult();
}
private static object FromJsonResult(object source,
Type targetType,
ref object targetInstance,
bool includeNonPublic)
{
return new Converter(source, targetType, ref targetInstance, includeNonPublic).GetResult();
}
private static Type GetAddMethodParameterType(Type targetType)
=> ListAddMethodCache.GetOrAdd(targetType,
x => x.GetMethods()
.FirstOrDefault(
m => m.Name.Equals(AddMethodName) && m.IsPublic && m.GetParameters().Length == 1)?
.GetParameters()[0]
.ParameterType);
private static void GetByteArray(string sourceString, ref object target)
{
try
{
target = Convert.FromBase64String(sourceString);
} // Try conversion from Base 64
catch
{
target = Encoding.UTF8.GetBytes(sourceString);
} // Get the string bytes in UTF8
}
private static object GetSourcePropertyValue(IDictionary sourceProperties,
MemberInfo targetProperty)
{
var targetPropertyName = MemberInfoNameCache.GetOrAdd(
targetProperty,
x => Runtime.AttributeCache.RetrieveOne(x)?.PropertyName ?? x.Name);
return sourceProperties.GetValueOrDefault(targetPropertyName);
}
private bool TrySetInstance(object targetInstance, object source, ref object target)
{
if (targetInstance == null)
{
// Try to create a default instance
try
{
source.CreateTarget(_targetType, _includeNonPublic, ref target);
}
catch
{
return false;
}
}
else
{
target = targetInstance;
}
return true;
}
private object GetResult() => _target ?? _targetType.GetDefault();
private void ResolveObject(object source, ref object target)
{
switch (source)
{
// Case 0: Special Cases Handling (Source and Target are of specific convertible types)
// Case 0.1: Source is string, Target is byte[]
case string sourceString when _targetType == typeof(byte[]):
GetByteArray(sourceString, ref target);
break;
// Case 1.1: Source is Dictionary, Target is IDictionary
case Dictionary sourceProperties when target is IDictionary targetDictionary:
PopulateDictionary(sourceProperties, targetDictionary);
break;
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
case Dictionary sourceProperties:
PopulateObject(sourceProperties);
break;
// Case 2.1: Source is List, Target is Array
case List