/// <summary> /// Parses a JSON snippet of an array value. /// </summary> /// <typeparam name="T">The type of the object to parse the array items into.</typeparam> /// <param name="json">The string containing the JSON snippet, begining and ending with square brackets.</param> /// <param name="typeResolver">A class to help resolving dynamic or unknown types.</param> /// <returns>The parsed array.</returns> public static T[] LoadArray <T>(string json, JsonTypeResolver typeResolver) { int i = 0; IList list = ReadArray(json, ref i, typeof(T[]), typeResolver ?? JsonTypeResolver.FromAssemblyMappings <T>(), true); T[] array = new T[list.Count]; list.CopyTo(array, 0); return(array); }
private static List <object> ReadArray(string s, ref int i, Type expectedValueType, JsonTypeResolver typeResolver, bool standalone = false) { global::System.Diagnostics.Debug.Assert(s != null); global::System.Diagnostics.Debug.Assert(i >= 0 && i < s.Length); global::System.Diagnostics.Debug.Assert(s[i] == '['); List <object> values = new List <object>(); Type expectedArrayValueType = null; if (expectedValueType != null) { Type[] listTypes; if (expectedValueType.HasElementType) { expectedArrayValueType = expectedValueType.GetElementType(); } else if (expectedValueType.Implements(typeof(IList <>), out listTypes)) { expectedArrayValueType = listTypes[0].GenericTypeArguments[0]; } } ++i; EnsureNotEndOfString(s, i); SkipWhitespace(s, ref i); if (s[i] != ']') { while (true) { object arrayValue = ReadValue(s, ref i, null, expectedArrayValueType, typeResolver); values.Add(arrayValue); SkipWhitespace(s, ref i); if (s[i] == ']') { break; } ReadNextMemberSeparator(s, ref i); SkipWhitespace(s, ref i); } } ++i; //if (!standalone) // EnsureNotEndOfString(s, i); return(values); }
private static object ReadValue(string s, ref int i, object value, Type expectedValueType, JsonTypeResolver typeResolver) { JsonValueType jsonType = ReadValueType(s, ref i); switch (jsonType) { case JsonValueType.String: value = ReadString(s, ref i); break; case JsonValueType.Number: value = ReadNumber(s, ref i); break; case JsonValueType.Object: if (value == null) { if (expectedValueType != null) { ConstructorInfo memberCtor = expectedValueType.GetConstructor(new Type[0]); if (memberCtor == null) { throw new FormatException("A type with parameter-less constructor is required at position " + i + ". If not available, supply an instance as the member's value."); } value = memberCtor.Invoke(null); } else { value = new Dictionary <string, object>(); } } LoadFromJson(value, s, ref i, typeResolver); break; case JsonValueType.Array: List <object> arrayValues = ReadArray(s, ref i, expectedValueType, typeResolver); if (expectedValueType != null && expectedValueType.IsArray) { Array array = Array.CreateInstance(expectedValueType.GetElementType(), arrayValues.Count); object[] genericArray = arrayValues.ToArray(); genericArray.CopyTo(array, 0); value = array; } else if (value is IList) { IList valueList = (IList)value; try { valueList.Clear(); foreach (object arrayValue in arrayValues) { valueList.Add(arrayValue); } } catch (Exception e) { global::System.Diagnostics.Debug.WriteLine("Array values could not be loaded at position {0}. Skipping. ({1})", i, e.Message); } } else if (expectedValueType == null || expectedValueType.IsAssignableFrom(typeof(IList))) { value = arrayValues; } else if (expectedValueType.Implements <IList>()) { ConstructorInfo memberCtor = expectedValueType.GetConstructor(new Type[0]); if (memberCtor == null) { throw new FormatException("A type with parameter-less constructor is required at position " + i + ". If not available, supply an instance as the member's value."); } IList valueList = (IList)memberCtor.Invoke(null); try { valueList.Clear(); foreach (object arrayValue in arrayValues) { valueList.Add(arrayValue); } } catch (Exception e) { global::System.Diagnostics.Debug.WriteLine("Array values could not be loaded at position {0}. Skipping. ({1})", i, e.Message); } value = valueList; } else { throw new NotSupportedException("'" + expectedValueType + "' is not a supported type for loading array values."); } break; case JsonValueType.True: value = true; break; case JsonValueType.False: value = false; break; case JsonValueType.Null: value = null; break; default: throw new NotSupportedException("Unsupported JSON type."); } return(value); }
/// <summary> /// Parses a JSON snippet of a value. /// </summary> /// <typeparam name="T">The type of the object to parse.</typeparam> /// <param name="json">The string containing the JSON snippet.</param> /// <param name="typeResolver">A class to help resolving dynamic or unknown types.</param> /// <returns>The parsed value.</returns> public static T LoadObject <T>(string json, JsonTypeResolver typeResolver) where T : new() { int i = 0; return((T)ReadValue(json, ref i, null, typeof(T), typeResolver ?? JsonTypeResolver.FromAssemblyMappings <T>())); }
private static void LoadFromJson <T>(T target, string json, ref int i, JsonTypeResolver typeResolver) { if (target == null) { throw new ArgumentNullException("target"); } if (json == null) { throw new ArgumentNullException("json"); } Type targetType = typeof(T); if (target != null) { targetType = target.GetType(); } IDictionary <string, PropertyInfo> memberResolver = GetAttributeMemberResolver(targetType); List <ResolverPending> resolverPendings = new List <ResolverPending>(); SkipWhitespace(json, ref i); ReadObjectStart(json, ref i); do { SkipWhitespace(json, ref i); if (json[i] == '}') { break; } string memberName = ReadMemberName(json, ref i); SkipWhitespace(json, ref i); ReadMemberValueSeparator(json, ref i); SkipWhitespace(json, ref i); if (target is IDictionary) { IDictionary targetTable = (IDictionary)target; Type expectedTableValueType = null; if (targetTable is TypeInKeyHashtable) { expectedTableValueType = typeResolver.ResolveType(memberName); } targetTable.Add(memberName, ReadValue(json, ref i, null, expectedTableValueType, typeResolver)); goto nextMember; } PropertyInfo memberProperty = null; memberResolver.TryGetValue(memberName, out memberProperty); pendingMember: if (memberProperty == null || memberProperty.GetIndexParameters().Length > 0) { global::System.Diagnostics.Debug.WriteLine("JSON Parser: Member '{0}' not found on the object of type '{1}' or requires indices. Skipping.", memberName, targetType); ReadValue(json, ref i, null, null, typeResolver); goto nextMember; } Type expectedValueType = memberProperty.PropertyType; if (typeResolver != null) { foreach (JsonMemberTypeInAttribute attribute in memberProperty.GetCustomAttributes <JsonMemberTypeInAttribute>(true)) { PropertyInfo typeProperty = targetType.GetProperty(attribute.MemberName); if (typeProperty != null) { if (typeProperty.PropertyType == typeof(Type)) { expectedValueType = (Type)typeProperty.GetValue(target, null); } else if (typeProperty.PropertyType == typeof(string)) { expectedValueType = typeResolver.ResolveType((string)typeProperty.GetValue(target, null)); // ?? memberProperty.PropertyType; } if (expectedValueType == null) { ResolverPending pending = new ResolverPending { TypeProperty = typeProperty, MemberProperty = memberProperty, I = i }; if (!resolverPendings.Any(p => p == pending)) { resolverPendings.Add(pending); } ReadValue(json, ref i, null, null, null); goto nextMember; } } else { global::System.Diagnostics.Debug.WriteLine("Type property '{0}' not found on object of type '{1}'. Ignoring attribute.", attribute.MemberName, targetType.Name); } } } if (expectedValueType.Implements <IDictionary>() && memberProperty.GetCustomAttributes <JsonMemberTypeInKeyAttribute>(true).Any()) { expectedValueType = typeof(TypeInKeyHashtable); } object currentValue = memberProperty.CanRead ? memberProperty.GetValue(target, null) : null; object value = ReadValue(json, ref i, currentValue, expectedValueType, typeResolver); if (!memberProperty.CanWrite) { if (value != currentValue) { global::System.Diagnostics.Debug.WriteLine("Member '{0}' is read-only. Skipping.", memberName); } goto nextMember; } if (value != null) { Type valueType = value.GetType(); if (!memberProperty.PropertyType.IsAssignableFrom(valueType)) { try { value = System.Convert.ChangeType(value, memberProperty.PropertyType, Thread.CurrentThread.CurrentCulture); } catch (Exception e) { global::System.Diagnostics.Debug.WriteLine("Invalid value for member '{0}' of type '{1}'. Skipping. ({2})", memberName, value, e.Message); goto nextMember; } } } try { object oldValue = memberProperty.GetValue(target, null); memberProperty.SetValue(target, value, null); ResolverPending pending = resolverPendings.FirstOrDefault(p => p.TypeProperty == memberProperty); if (pending != null) { resolverPendings.Remove(pending); if (!object.Equals(oldValue, value)) { i = pending.I; memberProperty = pending.MemberProperty; global::System.Diagnostics.Debug.WriteLine("Second pass for resolving value type for '{0}'.", memberProperty); goto pendingMember; } else { global::System.Diagnostics.Debug.WriteLine("Value type for '{0}' cannot be resolved. Skipping.", memberProperty); } } } catch (Exception e) { global::System.Diagnostics.Debug.WriteLine("Cannot set value '{0}' to member '{1}'. Skipping. ({2}).", value, memberName, e.Message); goto nextMember; } nextMember: SkipWhitespace(json, ref i); if (json[i] == '}') { break; } else if (json[i] != ',') { throw new FormatException("Unexpected character '" + json[i] + "' at position " + i + "."); } ++i; } while (true); ++i; // move after curly bracket foreach (ResolverPending pending in resolverPendings) { global::System.Diagnostics.Debug.WriteLine("Cannot set value '{0}' because the expected type in '{1}' was not supplied. Skipping.", pending.MemberProperty, pending.TypeProperty); } }
/// <summary> /// Parses a JSON string into an existing object. /// </summary> /// <typeparam name="T">The type of the object to parse the JSON string into.</typeparam> /// <param name="this">The object to parse the JSON string into.</param> /// <param name="json">The JSON string to parse.</param> /// <param name="typeResolver">A class to help resolving dynamic or unknown types.</param> public static void LoadFromJson <T>(this T @this, string json, JsonTypeResolver typeResolver) { int start = 0; LoadFromJson(@this, json, ref start, typeResolver ?? JsonTypeResolver.FromAssemblyMappings <T>()); }