private static IEnumerable <DeferredEntry> ReadProperties(object target, DeserializationContext context, IReadOnlyDictionary <string, PropertyBinding> all) { var bufferPart = new BufferPart(); var reader = context.Reader; List <DeferredEntry> deferred = null; while (true) { var token = reader.Read(ref bufferPart, out var propertyName); if (token == JsonToken.ObjectEnd) { return(deferred); } if (token != JsonToken.String) { throw Exceptions.BadToken(reader, token, JsonToken.String); } var hasProperty = all.TryGetValue(propertyName, out var property); token = reader.Read(ref bufferPart, out var _); if (token != JsonToken.NameSeparator) { throw Exceptions.BadToken(reader, token, JsonToken.NameSeparator); } if (hasProperty) { ITypeSelector typeSelector = null; var shouldDeffer = context.TypeSelectors?.TryGetValue(property.Descriptor.Type, out typeSelector) == true; if (!shouldDeffer) { property.Setter(target, property.Descriptor.Reader(context)); } else { var entry = new DeferredEntry { Selector = typeSelector, Binding = property, Snapshot = reader.TakeSnapshot() }; ValueSkipper.SkipNext(reader); if (deferred == null) { deferred = new List <DeferredEntry>(); } deferred.Add(entry); } } else { ValueSkipper.SkipNext(reader); } token = reader.Read(ref bufferPart, out var _); if (token == JsonToken.ObjectEnd) { return(deferred); } if (token != JsonToken.ValueSeparator) { throw Exceptions.BadToken(reader, token, JsonToken.ValueSeparator); } } }
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; }); }