protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonClassInfo classInfo = state.Current.JsonClassInfo; if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract) { if (!TypeToConvert.IsAssignableFrom(RuntimeType)) { ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state); } state.Current.ReturnValue = new HashSet <TElement>(); } else { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoDeserializationConstructor(TypeToConvert, ref reader, ref state); } TCollection returnValue = (TCollection)classInfo.CreateObject() !; if (returnValue.IsReadOnly) { ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state); } state.Current.ReturnValue = returnValue; } }
protected override void CreateCollection(ref ReadStack state, JsonSerializerOptions options) { JsonClassInfo classInfo = state.Current.JsonClassInfo; if (TypeToConvert.IsInterface || TypeToConvert.IsAbstract) { if (!TypeToConvert.IsAssignableFrom(RuntimeType)) { ThrowHelper.ThrowNotSupportedException_DeserializeNoParameterlessConstructor(TypeToConvert); } state.Current.ReturnValue = new List <object?>(); } else { if (classInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoParameterlessConstructor(TypeToConvert); } TCollection returnValue = (TCollection)classInfo.CreateObject() !; if (returnValue.IsReadOnly) { ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(TypeToConvert); } state.Current.ReturnValue = returnValue; } }
protected override void CreateCollection(ref ReadStack state, JsonSerializerOptions options) { if (!TypeToConvert.IsAssignableFrom(RuntimeType)) { ThrowHelper.ThrowNotSupportedException_DeserializeNoParameterlessConstructor(TypeToConvert); } state.Current.ReturnValue = new List <TElement>(); }
protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { if (!TypeToConvert.IsAssignableFrom(RuntimeType)) { ThrowHelper.ThrowNotSupportedException_CannotPopulateCollection(TypeToConvert, ref reader, ref state); } state.Current.ReturnValue = new List <TElement>(); }
/// <summary> /// Initializes the state for polymorphic cases and returns the appropriate derived converter. /// </summary> internal JsonConverter?ResolvePolymorphicConverter(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options, ref ReadStack state) { Debug.Assert(!IsValueType); Debug.Assert(CanHaveMetadata); Debug.Assert(state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Type)); Debug.Assert(state.Current.PolymorphicSerializationState != PolymorphicSerializationState.PolymorphicReEntryStarted); Debug.Assert(jsonTypeInfo.PolymorphicTypeResolver?.UsesTypeDiscriminators == true); JsonConverter?polymorphicConverter = null; switch (state.Current.PolymorphicSerializationState) { case PolymorphicSerializationState.None: Debug.Assert(!state.IsContinuation); Debug.Assert(state.PolymorphicTypeDiscriminator != null); PolymorphicTypeResolver resolver = jsonTypeInfo.PolymorphicTypeResolver; if (resolver.TryGetDerivedJsonTypeInfo(state.PolymorphicTypeDiscriminator, out JsonTypeInfo? resolvedType)) { Debug.Assert(TypeToConvert.IsAssignableFrom(resolvedType.Type)); polymorphicConverter = state.InitializePolymorphicReEntry(resolvedType); if (!polymorphicConverter.CanHaveMetadata) { ThrowHelper.ThrowNotSupportedException_DerivedConverterDoesNotSupportMetadata(resolvedType.Type); } } else { state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; } state.PolymorphicTypeDiscriminator = null; break; case PolymorphicSerializationState.PolymorphicReEntrySuspended: polymorphicConverter = state.ResumePolymorphicReEntry(); Debug.Assert(TypeToConvert.IsAssignableFrom(polymorphicConverter.TypeToConvert)); break; case PolymorphicSerializationState.PolymorphicReEntryNotFound: Debug.Assert(state.Current.PolymorphicJsonTypeInfo is null); break; default: Debug.Fail("Unexpected PolymorphicSerializationState."); break; } return(polymorphicConverter); }
/// <summary> /// When overidden, constructs a new <see cref="JsonConverter{T}"/> instance. /// </summary> protected internal JsonConverter() { // Today only typeof(object) can have polymorphic writes. // In the future, this will be check for !IsSealed (and excluding value types). CanBePolymorphic = TypeToConvert == JsonClassInfo.ObjectType; IsValueType = TypeToConvert.IsValueType; CanBeNull = !IsValueType || TypeToConvert.IsNullableOfT(); IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly; if (HandleNull) { HandleNullOnRead = true; HandleNullOnWrite = true; } // For the HandleNull == false case, either: // 1) The default values are assigned in this type's virtual HandleNull property // or // 2) A converter overroad HandleNull and returned false so HandleNullOnRead and HandleNullOnWrite // will be their default values of false. CanUseDirectReadOrWrite = !CanBePolymorphic && IsInternalConverter && ClassType == ClassType.Value; }
internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T?value) { if (ConverterStrategy == ConverterStrategy.Value) { // A value converter should never be within a continuation. Debug.Assert(!state.IsContinuation); // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } value = default; return(true); } #if !DEBUG // For performance, only perform validation on internal converters on debug builds. if (IsInternalConverter) { if (state.Current.NumberHandling != null) { value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options); } else { value = Read(ref reader, typeToConvert, options); } } else #endif { JsonTokenType originalPropertyTokenType = reader.TokenType; int originalPropertyDepth = reader.CurrentDepth; long originalPropertyBytesConsumed = reader.BytesConsumed; if (state.Current.NumberHandling != null) { value = ReadNumberWithCustomHandling(ref reader, state.Current.NumberHandling.Value, options); } else { value = Read(ref reader, typeToConvert, options); } VerifyRead( originalPropertyTokenType, originalPropertyDepth, originalPropertyBytesConsumed, isValueConverter: true, ref reader); } if (options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.Preserve && CanBePolymorphic && value is JsonElement element) { // Edge case where we want to lookup for a reference when parsing into typeof(object) // instead of return `value` as a JsonElement. Debug.Assert(TypeToConvert == typeof(object)); if (JsonSerializer.TryGetReferenceFromJsonElement(ref state, element, out object?referenceValue)) { value = (T?)referenceValue; } } return(true); } bool success; // Remember if we were a continuation here since Push() may affect IsContinuation. bool wasContinuation = state.IsContinuation; #if DEBUG // DEBUG: ensure push/pop operations preserve stack integrity JsonTypeInfo originalJsonTypeInfo = state.Current.JsonTypeInfo; #endif state.Push(); Debug.Assert(TypeToConvert.IsAssignableFrom(state.Current.JsonTypeInfo.Type)); #if !DEBUG // For performance, only perform validation on internal converters on debug builds. if (IsInternalConverter) { if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead && !wasContinuation) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } // For perf and converter simplicity, handle null here instead of forwarding to the converter. value = default; success = true; } else { success = OnTryRead(ref reader, typeToConvert, options, ref state, out value); } } else #endif { if (!wasContinuation) { // For perf and converter simplicity, handle null here instead of forwarding to the converter. if (reader.TokenType == JsonTokenType.Null && !HandleNullOnRead) { if (!CanBeNull) { ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } value = default; state.Pop(true); #if DEBUG Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); #endif return(true); } Debug.Assert(state.Current.OriginalTokenType == JsonTokenType.None); state.Current.OriginalTokenType = reader.TokenType; Debug.Assert(state.Current.OriginalDepth == 0); state.Current.OriginalDepth = reader.CurrentDepth; } success = OnTryRead(ref reader, typeToConvert, options, ref state, out value); if (success) { if (state.IsContinuation) { // The resumable converter did not forward to the next converter that previously returned false. ThrowHelper.ThrowJsonException_SerializationConverterRead(this); } VerifyRead( state.Current.OriginalTokenType, state.Current.OriginalDepth, bytesConsumed: 0, isValueConverter: false, ref reader); // No need to clear state.Current.* since a stack pop will occur. } } state.Pop(success); #if DEBUG Debug.Assert(ReferenceEquals(originalJsonTypeInfo, state.Current.JsonTypeInfo)); #endif return(success); }
/// <summary> /// Initializes the state for polymorphic cases and returns the appropriate derived converter. /// </summary> internal JsonConverter?ResolvePolymorphicConverter(object value, JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options, ref WriteStack state) { Debug.Assert(!IsValueType); Debug.Assert(value != null && TypeToConvert.IsAssignableFrom(value.GetType())); Debug.Assert(CanBePolymorphic || jsonTypeInfo.PolymorphicTypeResolver != null); Debug.Assert(state.PolymorphicTypeDiscriminator is null); JsonConverter?polymorphicConverter = null; switch (state.Current.PolymorphicSerializationState) { case PolymorphicSerializationState.None: Debug.Assert(!state.IsContinuation); Type runtimeType = value.GetType(); if (jsonTypeInfo.PolymorphicTypeResolver is PolymorphicTypeResolver resolver) { Debug.Assert(CanHaveMetadata); if (resolver.TryGetDerivedJsonTypeInfo(runtimeType, out JsonTypeInfo? derivedJsonTypeInfo, out string?typeDiscriminatorId)) { polymorphicConverter = state.Current.InitializePolymorphicReEntry(derivedJsonTypeInfo); if (typeDiscriminatorId is not null) { if (!polymorphicConverter.CanHaveMetadata) { ThrowHelper.ThrowNotSupportedException_DerivedConverterDoesNotSupportMetadata(derivedJsonTypeInfo.Type); } state.PolymorphicTypeDiscriminator = typeDiscriminatorId; } } else { state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; } } else { Debug.Assert(CanBePolymorphic); if (runtimeType != TypeToConvert) { polymorphicConverter = state.Current.InitializePolymorphicReEntry(runtimeType, options); } else { state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; } } break; case PolymorphicSerializationState.PolymorphicReEntrySuspended: Debug.Assert(state.IsContinuation); polymorphicConverter = state.Current.ResumePolymorphicReEntry(); Debug.Assert(TypeToConvert.IsAssignableFrom(polymorphicConverter.TypeToConvert)); break; case PolymorphicSerializationState.PolymorphicReEntryNotFound: Debug.Assert(state.IsContinuation); break; default: Debug.Fail("Unexpected PolymorphicSerializationState."); break; } return(polymorphicConverter); }
public override MethodInfo[] GetAdditionalDataMethod() { var mi = TypeToConvert.GetMethod(nameof(TimeZoneInfo.GetAdjustmentRules)); return(new MethodInfo[] { mi }); }
/// <summary> /// Initializes the state for polymorphic cases and returns the appropriate derived converter. /// </summary> internal JsonConverter?ResolvePolymorphicConverter(object value, JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options, ref WriteStack state) { Debug.Assert(!IsValueType); Debug.Assert(value != null && TypeToConvert.IsAssignableFrom(value.GetType())); Debug.Assert(CanBePolymorphic || jsonTypeInfo.PolymorphicTypeResolver != null); Debug.Assert(state.PolymorphicTypeDiscriminator is null); JsonConverter?polymorphicConverter = null; switch (state.Current.PolymorphicSerializationState) { case PolymorphicSerializationState.None: Debug.Assert(!state.IsContinuation); if (state.IsPolymorphicRootValue && state.CurrentDepth == 0) { Debug.Assert(jsonTypeInfo.PolymorphicTypeResolver != null); // We're serializing a root-level object value whose runtime type uses type hierarchies. // For consistency with nested value handling, we want to serialize as-is without emitting metadata. state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; break; } Type runtimeType = value.GetType(); if (jsonTypeInfo.PolymorphicTypeResolver is PolymorphicTypeResolver resolver) { Debug.Assert(CanHaveMetadata); if (resolver.TryGetDerivedJsonTypeInfo(runtimeType, out JsonTypeInfo? derivedJsonTypeInfo, out object?typeDiscriminator)) { polymorphicConverter = state.Current.InitializePolymorphicReEntry(derivedJsonTypeInfo); if (typeDiscriminator is not null) { if (!polymorphicConverter.CanHaveMetadata) { ThrowHelper.ThrowNotSupportedException_DerivedConverterDoesNotSupportMetadata(derivedJsonTypeInfo.Type); } state.PolymorphicTypeDiscriminator = typeDiscriminator; } } else { state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; } } else { Debug.Assert(CanBePolymorphic); if (runtimeType != TypeToConvert) { polymorphicConverter = state.Current.InitializePolymorphicReEntry(runtimeType, options); } else { state.Current.PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryNotFound; } } break; case PolymorphicSerializationState.PolymorphicReEntrySuspended: Debug.Assert(state.IsContinuation); polymorphicConverter = state.Current.ResumePolymorphicReEntry(); Debug.Assert(TypeToConvert.IsAssignableFrom(polymorphicConverter.TypeToConvert)); break; case PolymorphicSerializationState.PolymorphicReEntryNotFound: Debug.Assert(state.IsContinuation); break; default: Debug.Fail("Unexpected PolymorphicSerializationState."); break; } return(polymorphicConverter); }