ln.json/ln.json/mapping/JSONMapper.cs

428 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
namespace ln.json.mapping
{
public delegate bool RequestCustomSerialization(object o, out JSONValue json);
public delegate bool RequestCustomUnserialization(JSONValue json, Type targetType, out object o);
public delegate bool RequestCustomMapping(Type targetType,out JSONMapping mapping);
public delegate bool MappingFactory(Type targetType, out JSONMapping mapping);
public class JSONMapper
{
public static JSONMapper DefaultMapper { get; set; } = new JSONMapper();
public event RequestCustomSerialization OnRequestCustomSerialization;
public event RequestCustomUnserialization OnRequestCustomUnserialization;
public event RequestCustomMapping OnRequestCustomMapping;
public JSONObjectMappingFlags DefaultMappingFlags { get; set; } = JSONObjectMappingFlags.PROPERTIES | JSONObjectMappingFlags.FIELDS;
public BindingFlags DefaultBindingFlags { get; set; } = BindingFlags.Instance | BindingFlags.Public;
public bool RequestCustomSerialization(object o, out JSONValue json)
{
foreach (RequestCustomSerialization rcs in OnRequestCustomSerialization?.GetInvocationList() ?? new RequestCustomSerialization[0])
{
if (rcs(o, out json))
return true;
}
json = null;
return false;
}
public bool RequestCustomUnserialization(JSONValue json, Type targetType, out object o)
{
foreach (RequestCustomUnserialization rcu in OnRequestCustomUnserialization?.GetInvocationList() ?? new RequestCustomUnserialization[0])
{
if (rcu(json, targetType, out o))
return true;
}
o = null;
return false;
}
public bool RequestCustomMapping(Type targetType, out JSONMapping mapping)
{
foreach (RequestCustomMapping rcm in OnRequestCustomMapping?.GetInvocationList() ?? new RequestCustomMapping[0])
{
if (rcm(targetType, out mapping))
return true;
}
mapping = null;
return false;
}
Dictionary<Type, JSONMapping> mappings = new Dictionary<Type, JSONMapping>();
public virtual void Add(JSONMapping mapping) => mappings[mapping.TargetType] = mapping;
Dictionary<Type, MappingFactory> mappingFactories = new Dictionary<Type, MappingFactory>();
public virtual void AddMappingFactory(Type targetType, MappingFactory mappingFactory) => mappingFactories.Add(targetType, mappingFactory);
public virtual bool GetOrBuildMapping(Type nativeType, out JSONMapping mapping) => TryGetMapping(nativeType, out mapping) || TryBuildRememberedMapping(nativeType, out mapping);
public virtual bool TryBuildRememberedMapping(Type nativeType,out JSONMapping mapping)
{
if (TryBuildMapping(nativeType, out mapping))
{
mappings.Add(nativeType, mapping);
return true;
}
return false;
}
public virtual bool TryBuildMapping(Type nativeType,out JSONMapping mapping)
{
if (nativeType.IsPrimitive)
{
mapping = null;
return false;
}
if (nativeType.IsArray)
{
mapping = new JSONArrayMapping(nativeType);
return true;
}
if (nativeType.IsSubclassOf(typeof(Exception)))
{
mapping = new JSONExceptionMapping(nativeType);
return true;
}
if ((mappingFactories.TryGetValue(nativeType, out MappingFactory mappingFactory)) && mappingFactory(nativeType, out mapping))
return true;
if (nativeType.IsGenericType)
{
Type genericTypeDefinition = nativeType.GetGenericTypeDefinition();
if (mappingFactories.TryGetValue(genericTypeDefinition, out mappingFactory) && mappingFactory(nativeType, out mapping))
return true;
if (genericTypeDefinition.Equals(typeof(IEnumerable<>)))
{
mapping = (JSONMapping)Activator.CreateInstance(typeof(JSONEnumerableMapping<>).MakeGenericType(nativeType.GetGenericArguments()[0]));
return true;
}
if (genericTypeDefinition.Equals(typeof(Dictionary<,>)))
{
mapping = (JSONMapping)Activator.CreateInstance(typeof(JSONDictionaryMapping<,>).MakeGenericType(nativeType.GetGenericArguments()));
return true;
}
}
mapping = new JSONObjectMapping(nativeType, DefaultMappingFlags, DefaultBindingFlags);
return true;
}
public virtual bool TryGetMapping(Type nativeType,out JSONMapping mapping)
{
if (mappings.TryGetValue(nativeType, out mapping))
return true;
if (RequestCustomMapping(nativeType, out mapping))
return true;
if ((this != DefaultMapper) && DefaultMapper.TryGetMapping(nativeType, out mapping))
return true;
return false;
}
public virtual bool Serialize(object o, out JSONValue json)
{
if (object.ReferenceEquals(null, o))
{
json = JSONNull.Instance;
return true;
}
Type type = o.GetType();
if (RequestCustomSerialization(o, out json))
return true;
if (TryGetMapping(type, out JSONMapping mapping))
{
json = mapping.ToJson(this, o);
return true;
}
if (type.IsEnum)
{
if (type.GetCustomAttribute<FlagsAttribute>() != null)
{
Type enumBaseType = type.GetEnumUnderlyingType();
o = Convert.ChangeType(o, enumBaseType);
ConstructorInfo constructor = typeof(JSONNumber).GetConstructor(new Type[]{ enumBaseType });
json = (JSONNumber)constructor.Invoke(new object[]{ o });
}
else
{
json = new JSONString(Enum.GetName(type, o));
}
return true;
}
if (type.IsPrimitive)
{
throw new NotSupportedException(String.Format("JSONMapperBase: Unsupported primitive type found: {0}", type));
}
if (TryBuildRememberedMapping(type,out mapping))
{
json = mapping.ToJson(this, o);
return true;
}
return false;
}
public virtual bool Deserialize<T>(string jsonSource, out T v) => Deserialize(JSONParser.Parse(jsonSource), out v);
public virtual bool Deserialize<T>(JSONValue json, out T v)
{
if (Deserialize(json, typeof(T), out object o))
{
v = (T)o;
return true;
}
v = default;
return false;
}
public virtual bool Deserialize(string jsonSource, Type nativeType, out object o) => Deserialize(JSONParser.Parse(jsonSource), nativeType, out o);
public virtual bool Deserialize(JSONValue json, Type nativeType, out object o)
{
o = null;
if (JSONNull.Instance.Equals(json))
return true;
if (RequestCustomUnserialization(json, nativeType, out o))
return true;
if (TryGetMapping(nativeType, out JSONMapping mapping))
{
o = mapping.FromJson(this, json);
return true;
}
if (nativeType.IsEnum)
{
if (json is JSONNumber jsonNumber)
o = Enum.ToObject(nativeType, jsonNumber.ToNative());
else if (json is JSONString jsonString)
o = Enum.Parse(nativeType, jsonString.Value);
else
throw new NotSupportedException(String.Format("Mapping from {0} to {1} is not supported", json.GetType().Name, nativeType.Name));
return true;
}
if (nativeType.IsPrimitive)
{
throw new NotSupportedException(string.Format("JSONMapperBase: Unsupported primitive type found: {0}", nativeType));
}
if (TryBuildRememberedMapping(nativeType, out mapping))
{
o = mapping.FromJson(this, json);
return true;
}
return false;
}
public void Apply(string jsonString, object o) => Apply((JSONObject)JSONParser.Parse(jsonString), o);
public virtual bool Apply(JSONObject json, object o)
{
Type nativeType = o.GetType();
if (TryGetMapping(nativeType,out JSONMapping mapping) || TryBuildRememberedMapping(nativeType, out mapping))
{
JSONObjectMapping objectMapping = mapping as JSONObjectMapping;
objectMapping.Apply(json, o);
return true;
}
return false;
}
public virtual JSONValue ToJson(object o)
{
if (Serialize(o,out JSONValue json))
return json;
throw new NotSupportedException();
}
public virtual T FromJson<T>(JSONValue json) => (T)FromJson(json, typeof(T));
public virtual object FromJson(JSONValue json,Type targetType)
{
if (Deserialize(json, targetType, out object o))
return o;
throw new NotSupportedException();
}
public bool MapMethodParameters(MethodInfo methodInfo, JSONValue jsonArguments, out object[] methodArguments)
{
if (jsonArguments is JSONArray jsonArrayArguments)
return MapMethodParameters(methodInfo.GetParameters(), jsonArrayArguments, out methodArguments);
if (jsonArguments is JSONObject jsonNamedArguments)
return MapMethodParameters(methodInfo.GetParameters(), jsonNamedArguments, out methodArguments);
throw new ArgumentException(nameof(jsonArguments));
}
public bool MapMethodParameters(ParameterInfo[] parameterInfos, JSONValue jsonArguments, out object[] methodArguments)
{
if (jsonArguments is JSONArray jsonArrayArguments)
return MapMethodParameters(parameterInfos, jsonArrayArguments, out methodArguments);
if (jsonArguments is JSONObject jsonNamedArguments)
return MapMethodParameters(parameterInfos, jsonNamedArguments, out methodArguments);
throw new ArgumentException(nameof(jsonArguments));
}
public bool MapMethodParameters(MethodInfo methodInfo, JSONObject namedArguments, out object[] methodArguments) => MapMethodParameters(methodInfo.GetParameters(), namedArguments, out methodArguments);
public bool MapMethodParameters(ParameterInfo[] parameterInfos, JSONObject namedArguments, out object[] methodArguments)
{
methodArguments = new object[parameterInfos.Length];
for (int n=0; n < methodArguments.Length; n++)
{
if (namedArguments.ContainsKey(parameterInfos[n].Name))
{
if (!JSONMapper.DefaultMapper.Deserialize(namedArguments[parameterInfos[n].Name], parameterInfos[n].ParameterType, out methodArguments[n]))
return false;
} else if (parameterInfos[n].IsOptional)
{
methodArguments[n] = parameterInfos[n].DefaultValue;
} else
return false;
}
return true;
}
public bool MapMethodParameters(MethodInfo methodInfo, JSONArray jsonArguments, out object[] methodArguments) => MapMethodParameters(methodInfo.GetParameters(), jsonArguments, out methodArguments);
public bool MapMethodParameters(ParameterInfo[] parameterInfos, JSONArray jsonArguments, out object[] methodArguments)
{
methodArguments = new object[parameterInfos.Length];
if (jsonArguments.Count != methodArguments.Length)
return false;
for (int n=0; n < methodArguments.Length; n++)
{
if (!JSONMapper.DefaultMapper.Deserialize(jsonArguments[n], parameterInfos[n].ParameterType, out methodArguments[n]))
return false;
}
return true;
}
static JSONMapper()
{
DefaultMapper.Add(new JSONMapping(
typeof(byte),
(JSONMapper arg1, object arg2) => new JSONNumber((int)(byte)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToByte(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(sbyte),
(JSONMapper arg1, object arg2) => new JSONNumber((int)(sbyte)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToSByte(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(short),
(JSONMapper arg1, object arg2) => new JSONNumber((int)(short)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToInt16(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(int),
(JSONMapper arg1, object arg2) => new JSONNumber((int)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToInt32(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(long),
(JSONMapper arg1, object arg2) => new JSONNumber((long)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToInt64(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(ushort),
(JSONMapper arg1, object arg2) => new JSONNumber((uint)(ushort)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToUInt16(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(uint),
(JSONMapper arg1, object arg2) => new JSONNumber((uint)(uint)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToUInt32(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(ulong),
(JSONMapper arg1, object arg2) => new JSONNumber((ulong)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToUInt64(((JSONNumber)arg2).Decimal)
));
/**
* Float
**/
DefaultMapper.Add(new JSONMapping(
typeof(float),
(JSONMapper arg1, object arg2) => new JSONNumber((float)arg2),
(JSONMapper arg1, JSONValue arg2) => (float)Decimal.ToDouble(((JSONNumber)arg2).Decimal)
));
DefaultMapper.Add(new JSONMapping(
typeof(double),
(JSONMapper arg1, object arg2) => new JSONNumber((double)arg2),
(JSONMapper arg1, JSONValue arg2) => Decimal.ToDouble(((JSONNumber)arg2).Decimal)
));
/**
* Strings
**/
DefaultMapper.Add(new JSONMapping(
typeof(string),
(JSONMapper arg1, object arg2) => new JSONString((string)arg2),
(JSONMapper arg1, JSONValue arg2) => ((JSONString)arg2).Value
));
/**
* Others
**/
DefaultMapper.Add(new JSONMapping(
typeof(char),
(JSONMapper arg1, object arg2) => new JSONString(new String(new char[] { (char)arg2 })),
(JSONMapper arg1, JSONValue arg2) => ((JSONString)arg2).Value[0]
));
DefaultMapper.Add(new JSONMapping(
typeof(bool),
(JSONMapper arg1, object arg2) => ((bool)arg2) ? (JSONValue)JSONTrue.Instance : (JSONValue)JSONFalse.Instance,
(JSONMapper arg1, JSONValue arg2) => (arg2.ValueType == JSONValueType.TRUE) || (arg2.ValueType == JSONValueType.FALSE) ? false : throw new NotSupportedException()
));
DefaultMapper.Add(new JSONByteArrayMapping());
DefaultMapper.Add(new JSONDateTimeMapping());
DefaultMapper.Add(new JSONDateTimeOffsetMapping());
DefaultMapper.Add(new JSONGuidMapping());
DefaultMapper.Add(new JSONTimeSpanMapping());
DefaultMapper.Add(new JSONIPv6Mapping());
DefaultMapper.Add(new JSONRPCCallMapping());
DefaultMapper.Add(new JSONRPCResultMapping());
}
}
}