private JsonMaker MakeJsonMaker(Type objectType) { // Specific custom deserialization JsonMaker customMaker = _TranslatorExtensions?.MakeJsonMaker(objectType); if (customMaker != null) { return(customMaker); } // String if (objectType == typeof(string)) { return(obj => { if (obj == null) { return JsonObject.Null; } else { return new JsonObject(obj as string); } }); } // Nullable types if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Nullable <>)) { JsonMaker subObjectMaker = MakeJsonMaker(objectType.GetGenericArguments()[0]); return(obj => { if (obj == null) { return JsonObject.Null; } else { return subObjectMaker(obj); } }); } // Enums if (objectType.IsEnum) { return(obj => new JsonObject(obj.ToString())); } // Simple types foreach (var kvp in SIMPLE_TYPES) { if (objectType == kvp.Key) { ConstructorInfo jtc = typeof(JsonObject).GetConstructor(new Type[] { kvp.Key }); return(obj => (JsonObject)jtc.Invoke(new object[] { obj })); } } // Parseable from string Func <string, object> parser = JsonReflection.GetParser(objectType); if (parser != null) { Func <object, string> toString = JsonReflection.GetToString(objectType); return(obj => new JsonObject(toString(obj))); } // Explicit Dictionary JsonReflection.DictionaryInfo dinfo = JsonReflection.IsDictionary(objectType); if (dinfo != null) { if (JsonReflection.GetParser(dinfo.KeyType) == null) { throw new InvalidOperationException("Cannot create JsonMaker for type " + objectType.FullName + " because key type " + dinfo.KeyType.FullName + " cannot be parsed from a string"); } JsonMaker valueMaker = GetJsonMaker(dinfo.ValueType); Type kvpType = typeof(KeyValuePair <,>).MakeGenericType(new Type[] { dinfo.KeyType, dinfo.ValueType }); PropertyInfo keyProperty = kvpType.GetProperty("Key"); PropertyInfo valueProperty = kvpType.GetProperty("Value"); return(obj => { if (obj == null) { return JsonObject.Null; } var result = JsonObject.EmptyDictionary; foreach (var kvp in obj as IEnumerable) { result[keyProperty.GetValue(kvp).ToString()] = valueMaker(valueProperty.GetValue(kvp)); } return result; }); } // Array Type contentType = JsonReflection.GetEnumerableType(objectType); if (contentType != null) { return(obj => { if (obj == null) { return JsonObject.Null; } JsonMaker itemMaker = GetJsonMaker(contentType); var items = new List <JsonObject>(); foreach (object item in (IEnumerable)obj) { items.Add(itemMaker(item)); } return new JsonObject(items); }); } // Object-as-Dictionary (choice of last resort) object defaultObj = JsonReflection.GetDefaultMaker(objectType)(); var getters = new Dictionary <string, MemberGetter>(); var types = new Dictionary <string, Type>(); var equalities = new Dictionary <string, MethodInfo>(); FieldInfo[] fields = objectType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (FieldInfo fi in fields) { if (fi.GetCustomAttribute <JsonIgnoreAttribute>() != null) { continue; } string name = AdjustedFieldName(fi.Name); getters[name] = obj => fi.GetValue(obj); types[name] = fi.FieldType; equalities[name] = fi.FieldType.GetMethod("Equals", new Type[] { typeof(object) }); } return(obj => { var jfields = new Dictionary <string, JsonObject>(); foreach (var kvp in getters) { object objVal = kvp.Value(obj); object defVal = kvp.Value(defaultObj); MethodInfo equality = equalities[kvp.Key]; bool equal = defVal == null ? objVal == null : (bool)equality.Invoke(defVal, new object[] { objVal }); if (!equal) { jfields[kvp.Key] = GetJsonMaker(types[kvp.Key])(objVal); } } return new JsonObject(jfields); }); }
private ObjectMaker MakeObjectMaker(Type objectType) { // Specific custom deserialization ObjectMaker customMaker = _TranslatorExtensions?.MakeObjectMaker(objectType); if (customMaker != null) { return(customMaker); } // String if (objectType == typeof(string)) { return(json => { if (json.ObjectType == JsonObject.Type.Null) { return null; } else { return json.String; } }); } // Nullable types if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Nullable <>)) { ObjectMaker subObjectMaker = MakeObjectMaker(objectType.GetGenericArguments()[0]); return(json => { if (json.ObjectType == JsonObject.Type.Null) { return null; } else { return subObjectMaker(json); } }); } // Enums if (objectType.IsEnum) { return(json => { if (json.ObjectType != JsonObject.Type.String) { throw new FormatException("Invalid JSON: Expected JSON String for .NET Enum type " + objectType.FullName + " but found instead " + json.ObjectType); } return Enum.Parse(objectType, json.String); }); } // Simple types if (SIMPLE_TYPES.ContainsKey(objectType)) { JsonObject.Type[] allowableTypes = SIMPLE_TYPES[objectType]; return(json => { if (allowableTypes.Contains(json.ObjectType)) { return JsonReflection.Cast(json.Value, objectType); } else { throw new FormatException("Invalid JSON: Expected JSON " + allowableTypes.Select(t => t.ToString()).Aggregate((a, b) => a + " or " + b) + " for .NET " + objectType.Name + "; instead found JSON " + json.ObjectType); } }); } // Explicit Dictionary JsonReflection.DictionaryInfo dinfo = JsonReflection.IsDictionary(objectType); if (dinfo != null) { Func <object> dictConstructor = JsonReflection.GetDefaultMaker(objectType); Func <string, object> keyParser = JsonReflection.GetParser(dinfo.KeyType); if (keyParser == null) { throw new InvalidOperationException("Cannot create JsonMaker for type " + objectType.FullName + " because key type " + dinfo.KeyType.FullName + " cannot be parsed from a string"); } ObjectMaker valueMaker = GetObjectMaker(dinfo.ValueType); MethodInfo add = objectType.GetMethod("Add", new Type[] { dinfo.KeyType, dinfo.ValueType }); return(json => { if (json.ObjectType == JsonObject.Type.Null) { return null; } var result = dictConstructor(); foreach (var kvp in json.Dictionary) { add.Invoke(result, new object[] { keyParser(kvp.Key), valueMaker(kvp.Value) }); } return result; }); } // Enumerable types if (typeof(IEnumerable).IsAssignableFrom(objectType)) { // Arrays if (typeof(Array).IsAssignableFrom(objectType)) { Type itemType = objectType.GetElementType(); return(json => { if (json.ObjectType == JsonObject.Type.Null) { return null; } if (json.ObjectType != JsonObject.Type.Array) { throw new FormatException("Invalid JSON: Expected JSON Array for .NET type " + objectType.FullName + " but found instead " + json.ObjectType); } JsonObject[] jitems = json.Array; Array result = (Array)Activator.CreateInstance(objectType, new object[] { jitems.Length }); ObjectMaker itemMaker = GetObjectMaker(itemType); int i = 0; foreach (JsonObject jitem in jitems) { result.SetValue(itemMaker(jitem), i); i++; } return result; }); } // Other enumerable classes Func <object> enumConstructor = JsonReflection.GetDefaultMaker(objectType); if (enumConstructor == null) { throw new InvalidOperationException("Cannot create JSON Array ObjectMaker for type " + objectType.FullName + " because it does not have a default constructor"); } Type contentType = JsonReflection.GetEnumerableType(objectType); MethodInfo addMethod = objectType.GetMethod("Add", new Type[] { contentType }); if (enumConstructor == null) { throw new InvalidOperationException("Cannot create JSON Array ObjectMaker for type " + objectType.FullName + " because it does not have an Add method"); } return(json => { if (json.ObjectType == JsonObject.Type.Null) { return null; } ObjectMaker itemMaker = GetObjectMaker(contentType); object result = enumConstructor(); foreach (JsonObject jitem in json.Array) { addMethod.Invoke(result, new object[] { itemMaker(jitem) }); } return result; }); } // Parseable from string Func <string, object> parser = JsonReflection.GetParser(objectType); if (parser != null) { return(json => { if (json.ObjectType == JsonObject.Type.String || json.ObjectType == JsonObject.Type.Null) { return parser(json.String); } else { throw new FormatException("Invalid JSON: Expected parseable JSON String for .NET type " + objectType.FullName + "; instead found JSON " + json.ObjectType); } }); } // Object-as-Dictionary (choice of last resort) Func <object> ci = JsonReflection.GetDefaultMaker(objectType); if (ci == null) { throw new InvalidOperationException("Cannot create JSON Dictionary ObjectMaker for type " + objectType.FullName + " because a default object cannot be created"); } var setters = new Dictionary <string, MemberSetter>(); var types = new Dictionary <string, Type>(); FieldInfo[] fields = objectType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (FieldInfo fi in fields) { if (fi.GetCustomAttribute <JsonIgnoreAttribute>() != null) { continue; } string name = AdjustedFieldName(fi.Name); types[name] = fi.FieldType; setters[name] = (obj, value) => fi.SetValue(obj, value); } //PropertyInfo[] properties = objectType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); //foreach (PropertyInfo pi in properties) //{ // if (pi.GetCustomAttribute<JsonIgnoreAttribute>() != null) // continue; // throw new NotImplementedException("Property serialization not yet supported"); //} return(json => { if (json.ObjectType != JsonObject.Type.Dictionary) { throw new FormatException("Invalid JSON: Expected JSON Dictionary for .NET type " + objectType.FullName + "; instead found JSON " + json.ObjectType); } object result = ci(); Dictionary <string, JsonObject> jfields = json.Dictionary; foreach (var kvp in jfields) { if (!setters.ContainsKey(kvp.Key)) { throw new FormatException("No field named '" + kvp.Key + "' found in .NET type " + objectType.FullName); } ObjectMaker maker = GetObjectMaker(types[kvp.Key]); setters[kvp.Key](result, maker(kvp.Value)); } return result; }); }