using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
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 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 readonly JsonSerializerCase _jsonSerializerCase;
private Converter(
object? source,
Type targetType,
ref object? targetInstance,
bool includeNonPublic,
JsonSerializerCase jsonSerializerCase)
{
_targetType = targetInstance != null ? targetInstance.GetType() : targetType;
_includeNonPublic = includeNonPublic;
_jsonSerializerCase = jsonSerializerCase;
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);
}
internal static object? FromJsonResult(
object? source,
JsonSerializerCase jsonSerializerCase,
Type? targetType = null,
bool includeNonPublic = false)
{
object? nullRef = null;
return new Converter(source, targetType ?? typeof(object), ref nullRef, includeNonPublic, jsonSerializerCase).GetResult();
}
private static object? FromJsonResult(object source,
Type targetType,
ref object? targetInstance,
bool includeNonPublic)
{
return new Converter(source, targetType, ref targetInstance, includeNonPublic, JsonSerializerCase.None).GetResult();
}
private static Type? GetAddMethodParameterType(Type targetType)
=> ListAddMethodCache.GetOrAdd(targetType,
x => x.GetMethods()
.FirstOrDefault(
m => m.Name == 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 (FormatException)
{
target = Encoding.UTF8.GetBytes(sourceString);
} // Get the string bytes in UTF8
}
private object GetSourcePropertyValue(
IDictionary sourceProperties,
MemberInfo targetProperty)
{
var targetPropertyName = MemberInfoNameCache.GetOrAdd(
targetProperty,
x => AttributeCache.DefaultCache.Value.RetrieveOne(x)?.PropertyName ?? x.Name.GetNameWithCase(_jsonSerializerCase));
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