private static Func <DeserializationContext, object> ForComplex(Type type, TypeOptions options, Func <Type, TypeDescriptor> descriptorSource) { var info = type.GetTypeInfo(); if (info.IsClass && info.GetConstructor(Type.EmptyTypes) == null) { return(reader => throw new JsonException(info.IsAbstract ? $"Type {type} is abstract. Add {nameof(ITypeSelector)} to {nameof(SerializationSettings)} in order to deserialize this type." : $"Type {type} must define public parameterless constructor.")); } var properties = info .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(i => i.SetMethod != null && i.GetMethod != null) .Select(i => new { Property = (MemberInfo)i, Setter = ReflectionUtils.BuildSetter(i), Descriptor = descriptorSource(i.PropertyType) }); var fields = info .GetFields(BindingFlags.Instance | BindingFlags.Public) .Select(i => new { Property = (MemberInfo)i, Setter = ReflectionUtils.BuildFieldSetter(i), Descriptor = descriptorSource(i.FieldType) }); var all = properties .Concat(fields) .SelectMany(i => JsonNames(i.Property).Select(j => new PropertyBinding { Name = j, Setter = i.Setter, Descriptor = i.Descriptor })) .GroupBy(i => i.Name) .ToDictionary(i => i.Key, i => i.First()); var constructor = info.IsClass ? ReflectionUtils.BuildConstructor(type) : () => Activator.CreateInstance(typeof(Box <>).MakeGenericType(type)); //struct types are internally wrapped into Box<T> type (see ReflectionUtils) var unwrapper = !info.IsClass ? new Func <object, object>(i => ((IBox)i).Value) : null; return(context => { var bufferPart = new BufferPart(); var reader = context.Reader; var token = reader.Read(ref bufferPart, out var _); if (token == JsonToken.Null) { return info.IsClass ? (object)null : throw new JsonException($"Unable to assign null value to struct type near line {reader.Line}, column {reader.Column}."); } if (token != JsonToken.ObjectStart) { throw Exceptions.BadToken(reader, token, JsonToken.ObjectStart); } var target = constructor(); var deferred = ReadProperties(target, context, all); var unwrapped = unwrapper != null?unwrapper(target) : target; if (deferred == null) { return unwrapped; } foreach (var entry in deferred) { var runtimeType = entry.Selector.FindPropertyType(entry.Binding.Name, unwrapped); var descriptor = context.Catalog.GetDescriptor(runtimeType, options); using (reader.LoadSnapshot(entry.Snapshot)) entry.Binding.Setter(target, descriptor.Reader(context)); } return unwrapper != null?unwrapper(target) : target; }); }