internal abstract void SetValueAsObject(object obj, object value, JsonSerializerOptions options);
internal abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer);
internal abstract void ApplyNullValue(JsonSerializerOptions options, ref ReadStack state);
internal abstract void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
private void DetermineSerializationCapabilities(JsonSerializerOptions options) { bool hasIgnoreAttribute = (GetAttribute <JsonIgnoreAttribute>() != null); if (hasIgnoreAttribute) { // We don't serialize or deserialize. return; } if (ClassType != ClassType.Enumerable) { // We serialize if there is a getter + no [Ignore] attribute + not ignoring readonly properties. ShouldSerialize = HasGetter && (HasSetter || !options.IgnoreReadOnlyProperties); // We deserialize if there is a setter + no [Ignore] attribute. ShouldDeserialize = HasSetter; } else { if (HasGetter) { if (HasSetter) { ShouldDeserialize = true; } else if (RuntimePropertyType.IsAssignableFrom(typeof(IList))) { ShouldDeserialize = true; } //else //{ // // todo: future feature that allows non-List types (e.g. from System.Collections.Immutable) to have converters. //} } //else if (HasSetter) //{ // // todo: Special case where there is no getter but a setter (and an EnumerableConverter) //} if (ShouldDeserialize) { ShouldSerialize = HasGetter; if (RuntimePropertyType.IsArray) { EnumerableConverter = s_jsonArrayConverter; } else if (typeof(IEnumerable).IsAssignableFrom(RuntimePropertyType)) { Type elementType = JsonClassInfo.GetElementType(RuntimePropertyType); // If the property type only has interface(s) exposed by JsonEnumerableT<T> then use JsonEnumerableT as the converter. if (RuntimePropertyType.IsAssignableFrom(typeof(JsonEnumerableT <>).MakeGenericType(elementType))) { EnumerableConverter = s_jsonEnumerableConverter; } } } else { ShouldSerialize = HasGetter && !options.IgnoreReadOnlyProperties; } } }
internal abstract object GetValueAsObject(object obj, JsonSerializerOptions options);
internal virtual void GetPolicies(JsonSerializerOptions options) { DetermineSerializationCapabilities(options); DeterminePropertyName(options); IgnoreNullValues = options.IgnoreNullValues; }
private void DeterminePropertyName(JsonSerializerOptions options) { if (PropertyInfo != null) { JsonPropertyNameAttribute nameAttribute = GetAttribute <JsonPropertyNameAttribute>(); if (nameAttribute != null) { NameAsString = nameAttribute.Name; // This is detected and thrown by caller. if (NameAsString == null) { return; } } else if (options.PropertyNamingPolicy != null) { NameAsString = options.PropertyNamingPolicy.ConvertName(PropertyInfo.Name); // This is detected and thrown by caller. if (NameAsString == null) { return; } } else { NameAsString = PropertyInfo.Name; } // At this point propertyName is valid UTF16, so just call the simple UTF16->UTF8 encoder. _name = Encoding.UTF8.GetBytes(NameAsString); // Set the compare name. if (options.PropertyNameCaseInsensitive) { CompareNameAsString = NameAsString.ToUpperInvariant(); _compareName = Encoding.UTF8.GetBytes(CompareNameAsString); } else { CompareNameAsString = NameAsString; _compareName = _name; } // Cache the escaped name. int valueIdx = JsonWriterHelper.NeedsEscaping(_name); if (valueIdx == -1) { _escapedName = _name; } else { int length = JsonWriterHelper.GetMaxEscapedLength(_name.Length, valueIdx); byte[] tempArray = ArrayPool <byte> .Shared.Rent(length); JsonWriterHelper.EscapeString(_name, tempArray, valueIdx, out int written); _escapedName = new byte[written]; tempArray.CopyTo(_escapedName, 0); // We clear the array because it is "user data" (although a property name). new Span <byte>(tempArray, 0, written).Clear(); ArrayPool <byte> .Shared.Return(tempArray); } } }
/// <inheritdoc/> public override TimeSpan?Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.String) { throw ThrowHelper.GenerateJsonException_DeserializeUnableToConvertValue(typeof(TimeSpan?)); } string value = reader.GetString() !; try { return(TimeSpan.ParseExact(value, "c", CultureInfo.InvariantCulture)); } catch (Exception parseException) { throw ThrowHelper.GenerateJsonException_DeserializeUnableToConvertValue(typeof(TimeSpan?), value, parseException); } }
/// <inheritdoc/> public override void Write(Utf8JsonWriter writer, TimeSpan?value, JsonSerializerOptions options) => writer.WriteStringValue(value !.Value.ToString("c", CultureInfo.InvariantCulture));
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; state.Push(); #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); 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); return(success); }
/// <summary> /// Read and convert the JSON to T. /// </summary> /// <remarks> /// A converter may throw any Exception, but should throw <cref>JsonException</cref> when the JSON is invalid. /// </remarks> /// <param name="reader">The <see cref="Utf8JsonReader"/> to read from.</param> /// <param name="typeToConvert">The <see cref="Type"/> being converted.</param> /// <param name="options">The <see cref="JsonSerializerOptions"/> being used.</param> /// <returns>The value that was converted.</returns> public abstract T?Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options);
// Provide a default implementation for value converters. internal virtual bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T?value) { value = Read(ref reader, typeToConvert, options); return(true); }
// Provide a default implementation for value converters. internal virtual bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state) { Write(writer, value, options); return(true); }
// This non-generic API is sealed as it just forwards to the generic version. internal sealed override bool TryWriteAsObject(Utf8JsonWriter writer, object?value, JsonSerializerOptions options, ref WriteStack state) { T valueOfT = (T)value !; return(TryWrite(writer, valueOfT, options, ref state)); }