346 lines
13 KiB
C#
346 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
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)
|
|
{
|
|
json = new JSONNumber((int)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(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 (nativeType.GetCustomAttribute<FlagsAttribute>() != null)
|
|
{
|
|
o = Enum.ToObject(nativeType, (json as JSONNumber).AsInt);
|
|
}
|
|
else
|
|
{
|
|
o = Enum.Parse(nativeType, (json as JSONString).Value);
|
|
}
|
|
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();
|
|
}
|
|
|
|
|
|
static JSONMapper()
|
|
{
|
|
DefaultMapper.Add(new JSONMapping(
|
|
typeof(byte),
|
|
(JSONMapper arg1, object arg2) => new JSONNumber((int)arg2),
|
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToByte(((JSONNumber)arg2).Decimal)
|
|
));
|
|
DefaultMapper.Add(new JSONMapping(
|
|
typeof(short),
|
|
(JSONMapper arg1, object arg2) => new JSONNumber((int)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)arg2),
|
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToUInt16(((JSONNumber)arg2).Decimal)
|
|
));
|
|
DefaultMapper.Add(new JSONMapping(
|
|
typeof(uint),
|
|
(JSONMapper arg1, object arg2) => new JSONNumber((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 JSONRPCCallMapping());
|
|
DefaultMapper.Add(new JSONRPCResultMapping());
|
|
}
|
|
}
|
|
}
|