using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
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 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 Boolean _includeNonPublic;
private Converter(
Object source,
Type targetType,
ref Object targetInstance,
Boolean includeNonPublic) {
this._targetType = targetInstance != null ? targetInstance.GetType() : targetType;
this._includeNonPublic = includeNonPublic;
if(source == null) {
return;
}
Type sourceType = source.GetType();
if(this._targetType == null || this._targetType == typeof(Object)) {
this._targetType = sourceType;
}
if(sourceType == this._targetType) {
this._target = source;
return;
}
if(!this.TrySetInstance(targetInstance, source, ref this._target)) {
return;
}
this.ResolveObject(source, ref this._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,
Boolean includeNonPublic) {
Object nullRef = null;
return new Converter(source, targetType, ref nullRef, includeNonPublic).GetResult();
}
private static Object FromJsonResult(Object source,
Type targetType,
ref Object targetInstance,
Boolean includeNonPublic) => 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) {
String targetPropertyName = MemberInfoNameCache.GetOrAdd(
targetProperty,
x => Runtime.AttributeCache.RetrieveOne(x)?.PropertyName ?? x.Name);
return sourceProperties.GetValueOrDefault(targetPropertyName);
}
private Boolean TrySetInstance(Object targetInstance, Object source, ref Object target) {
if(targetInstance == null) {
// Try to create a default instance
try {
source.CreateTarget(this._targetType, this._includeNonPublic, ref target);
} catch {
return false;
}
} else {
target = targetInstance;
}
return true;
}
private Object GetResult() => this._target ?? this._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 this._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:
this.PopulateDictionary(sourceProperties, targetDictionary);
break;
// Case 1.2: Source is Dictionary, Target is not IDictionary (i.e. it is a complex type)
case Dictionary sourceProperties:
this.PopulateObject(sourceProperties);
break;
// Case 2.1: Source is List, Target is Array
case List