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 mappings = new Dictionary(); public virtual void Add(JSONMapping mapping) => mappings[mapping.TargetType] = mapping; Dictionary mappingFactories = new Dictionary(); 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() != 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() != 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(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()); } } }