private static void TypeInfoPropertiesDefaults_Generic <T>(JsonTypeInfo <T> ti) { if (ti.Kind == JsonTypeInfoKind.None) { Assert.Null(ti.CreateObject); Assert.Throws <InvalidOperationException>(() => ti.CreateObject = () => (T)Activator.CreateInstance(typeof(T))); } else { bool createObjCalled = false; Assert.NotNull(ti.CreateObject); Func <T> createObj = () => { createObjCalled = true; return(default(T)); }; ti.CreateObject = createObj; Assert.Same(createObj, ti.CreateObject); JsonTypeInfo untyped = ti; if (typeof(T).IsValueType) { Assert.NotSame(createObj, untyped.CreateObject); } else { Assert.Same(createObj, untyped.CreateObject); } Assert.Same(untyped.CreateObject, untyped.CreateObject); Assert.Same(createObj, ti.CreateObject); untyped.CreateObject(); Assert.True(createObjCalled); ti.CreateObject = null; Assert.Null(ti.CreateObject); Assert.Null(untyped.CreateObject); bool untypedCreateObjCalled = false; Func <object> untypedCreateObj = () => { untypedCreateObjCalled = true; return(default(T)); }; untyped.CreateObject = untypedCreateObj; Assert.Same(untypedCreateObj, untyped.CreateObject); Assert.Same(ti.CreateObject, ti.CreateObject); Assert.NotSame(untypedCreateObj, ti.CreateObject); ti.CreateObject(); Assert.True(untypedCreateObjCalled); untyped.CreateObject = null; Assert.Null(ti.CreateObject); Assert.Null(untyped.CreateObject); } }
public static void ModifiersAreCalledAndModifyTypeInfos() { DefaultJsonTypeInfoResolver r = new(); JsonTypeInfo storedTypeInfo = null; bool createObjectCalled = false; bool secondModifierCalled = false; r.Modifiers.Add((ti) => { Assert.Null(storedTypeInfo); storedTypeInfo = ti; // marker that test has modified something ti.CreateObject = () => { Assert.False(createObjectCalled); createObjectCalled = true; // we don't care what's returned as it won't be used by deserialization return(null); }; }); r.Modifiers.Add((ti) => { // this proves we've been called after first modifier Assert.NotNull(storedTypeInfo); Assert.Same(storedTypeInfo, ti); secondModifierCalled = true; }); JsonTypeInfo returnedTypeInfo = r.GetTypeInfo(typeof(InvalidOperationException), new JsonSerializerOptions()); Assert.NotNull(storedTypeInfo); Assert.Same(storedTypeInfo, returnedTypeInfo); Assert.False(createObjectCalled); // we call our previously set marker storedTypeInfo.CreateObject(); Assert.True(createObjectCalled); Assert.True(secondModifierCalled); }
internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, [MaybeNullWhen(false)] out T value) { JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo; object obj; if (!state.SupportContinuation && !state.Current.CanContainMetadata) { // Fast path that avoids maintaining state variables and dealing with preserved references. if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } if (jsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo.Type, ref reader, ref state); } obj = jsonTypeInfo.CreateObject() !; jsonTypeInfo.OnDeserializing?.Invoke(obj); // Process all properties. while (true) { // Read the property name or EndObject. reader.ReadWithVerify(); JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.EndObject) { break; } // Read method would have thrown if otherwise. Debug.Assert(tokenType == JsonTokenType.PropertyName); ReadOnlySpan <byte> unescapedPropertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options); JsonPropertyInfo jsonPropertyInfo = JsonSerializer.LookupProperty( obj, unescapedPropertyName, ref state, options, out bool useExtensionProperty); ReadPropertyValue(obj, ref state, ref reader, jsonPropertyInfo, useExtensionProperty); } } else { // Slower path that supports continuation and reading metadata. if (state.Current.ObjectState == StackFrameObjectState.None) { if (reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } state.Current.ObjectState = StackFrameObjectState.StartToken; } // Handle the metadata properties. if (state.Current.CanContainMetadata && state.Current.ObjectState < StackFrameObjectState.ReadMetadata) { if (!JsonSerializer.TryReadMetadata(this, jsonTypeInfo, ref reader, ref state)) { value = default; return(false); } if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref) { value = JsonSerializer.ResolveReferenceId <T>(ref state); return(true); } state.Current.ObjectState = StackFrameObjectState.ReadMetadata; } // Dispatch to any polymorphic converters: should always be entered regardless of ObjectState progress if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type) && state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted && ResolvePolymorphicConverter(jsonTypeInfo, options, ref state) is JsonConverter polymorphicConverter) { Debug.Assert(!IsValueType); bool success = polymorphicConverter.OnTryReadAsObject(ref reader, options, ref state, out object?objectResult); value = (T)objectResult !; state.ExitPolymorphicConverter(success); return(success); } if (state.Current.ObjectState < StackFrameObjectState.CreatedObject) { if (state.Current.CanContainMetadata) { JsonSerializer.ValidateMetadataForObjectConverter(this, ref reader, ref state); } if (state.Current.MetadataPropertyNames == MetadataPropertyName.Ref) { value = JsonSerializer.ResolveReferenceId <T>(ref state); return(true); } if (jsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo.Type, ref reader, ref state); } obj = jsonTypeInfo.CreateObject() !; if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Id)) { Debug.Assert(state.ReferenceId != null); Debug.Assert(options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve); state.ReferenceResolver.AddReference(state.ReferenceId, obj); state.ReferenceId = null; } jsonTypeInfo.OnDeserializing?.Invoke(obj); state.Current.ReturnValue = obj; state.Current.ObjectState = StackFrameObjectState.CreatedObject; } else { obj = state.Current.ReturnValue !; Debug.Assert(obj != null); } // Process all properties. while (true) { // Determine the property. if (state.Current.PropertyState == StackFramePropertyState.None) { state.Current.PropertyState = StackFramePropertyState.ReadName; if (!reader.Read()) { // The read-ahead functionality will do the Read(). state.Current.ReturnValue = obj; value = default; return(false); } } JsonPropertyInfo jsonPropertyInfo; if (state.Current.PropertyState < StackFramePropertyState.Name) { state.Current.PropertyState = StackFramePropertyState.Name; JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.EndObject) { break; } // Read method would have thrown if otherwise. Debug.Assert(tokenType == JsonTokenType.PropertyName); ReadOnlySpan <byte> unescapedPropertyName = JsonSerializer.GetPropertyName(ref state, ref reader, options); jsonPropertyInfo = JsonSerializer.LookupProperty( obj, unescapedPropertyName, ref state, options, out bool useExtensionProperty); state.Current.UseExtensionProperty = useExtensionProperty; } else { Debug.Assert(state.Current.JsonPropertyInfo != null); jsonPropertyInfo = state.Current.JsonPropertyInfo !; } if (state.Current.PropertyState < StackFramePropertyState.ReadValue) { if (!jsonPropertyInfo.CanDeserialize) { if (!reader.TrySkip()) { state.Current.ReturnValue = obj; value = default; return(false); } state.Current.EndProperty(); continue; } if (!ReadAheadPropertyValue(ref state, ref reader, jsonPropertyInfo)) { state.Current.ReturnValue = obj; value = default; return(false); } } if (state.Current.PropertyState < StackFramePropertyState.TryRead) { // Obtain the CLR value from the JSON and set the member. if (!state.Current.UseExtensionProperty) { if (!jsonPropertyInfo.ReadJsonAndSetMember(obj, ref state, ref reader)) { state.Current.ReturnValue = obj; value = default; return(false); } } else { if (!jsonPropertyInfo.ReadJsonAndAddExtensionProperty(obj, ref state, ref reader)) { // No need to set 'value' here since JsonElement must be read in full. state.Current.ReturnValue = obj; value = default; return(false); } } state.Current.EndProperty(); } } } jsonTypeInfo.OnDeserialized?.Invoke(obj); // Unbox Debug.Assert(obj != null); value = (T)obj; // Check if we are trying to build the sorted cache. if (state.Current.PropertyRefCache != null) { jsonTypeInfo.UpdateSortedPropertyCache(ref state.Current); } return(true); }