private static async Task WriteAsyncCore(object value, Type type, Stream utf8Json, JsonSerializerOptions options, CancellationToken cancellationToken) { if (options == null) { options = s_defaultSettings; } var writerState = new JsonWriterState(options.WriterOptions); using (var bufferWriter = new ArrayBufferWriter <byte>(options.DefaultBufferSize)) { if (value == null) { WriteNull(ref writerState, bufferWriter); #if BUILDING_INBOX_LIBRARY await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false); #else // todo: stackalloc or pool here? await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false); #endif return; } if (type == null) { type = value.GetType(); } JsonClassInfo classInfo = options.GetOrAddClass(type); WriteStack state = default; state.Current.JsonClassInfo = classInfo; state.Current.CurrentValue = value; if (classInfo.ClassType != ClassType.Object) { state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty(); } bool isFinalBlock; int flushThreshold; do { flushThreshold = (int)(bufferWriter.Capacity * .9); //todo: determine best value here isFinalBlock = Write(ref writerState, bufferWriter, flushThreshold, options, ref state); #if BUILDING_INBOX_LIBRARY await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false); #else // todo: use pool here to avod extra alloc? await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false); #endif bufferWriter.Clear(); } while (!isFinalBlock); } // todo: verify that we do want to call FlushAsync here (or above). It seems like leaving it to the caller would be best. }
private static async Task WriteAsyncCore(object value, Type type, Stream utf8Json, JsonSerializerOptions options, CancellationToken cancellationToken) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } JsonWriterOptions writerOptions = options.GetWriterOptions(); using (var bufferWriter = new PooledBufferWriter <byte>(options.DefaultBufferSize)) using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions)) { if (value == null) { writer.WriteNullValue(); writer.Flush(); #if BUILDING_INBOX_LIBRARY await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false); #else // todo: stackalloc or pool here? await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false); #endif return; } if (type == null) { type = value.GetType(); } WriteStack state = default; state.Current.Initialize(type, options); state.Current.CurrentValue = value; bool isFinalBlock; int flushThreshold; do { flushThreshold = (int)(bufferWriter.Capacity * .9); //todo: determine best value here isFinalBlock = Write(writer, flushThreshold, options, ref state); writer.Flush(); #if BUILDING_INBOX_LIBRARY await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false); #else // todo: use pool here to avod extra alloc? await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false); #endif bufferWriter.Clear(); } while (!isFinalBlock); } // todo: verify that we do want to call FlushAsync here (or above). It seems like leaving it to the caller would be best. }
internal sealed override bool WriteCoreAsObject( Utf8JsonWriter writer, object?value, JsonSerializerOptions options, ref WriteStack state) { Debug.Fail("We should never get here."); throw new InvalidOperationException(); }
internal sealed override bool TryWriteAsObject( Utf8JsonWriter writer, object?value, JsonSerializerOptions options, ref WriteStack state) { // We should never get here. Debug.Assert(false); throw new InvalidOperationException(); }
// There are three conditions to consider for an object (primitive value, enumerable or object) being processed here: // 1) The object type was specified as the root-level return type to a Parse\Read method. // 2) The object is property on a parent object. // 3) The object is an element in an enumerable. private static bool Write( Utf8JsonWriter writer, int flushThreshold, JsonSerializerOptions options, ref WriteStack state) { bool continueWriting = true; bool finishedSerializing; do { WriteStackFrame current = state.Current; switch (current.JsonClassInfo.ClassType) { case ClassType.Enumerable: finishedSerializing = HandleEnumerable(current.JsonClassInfo.ElementClassInfo, options, writer, ref state); break; case ClassType.Value: Debug.Assert(current.JsonPropertyInfo.ClassType == ClassType.Value); current.JsonPropertyInfo.Write(ref current, writer); finishedSerializing = true; break; case ClassType.Object: finishedSerializing = WriteObject(options, writer, ref state); break; case ClassType.Dictionary: case ClassType.ImmutableDictionary: finishedSerializing = HandleDictionary(current.JsonClassInfo.ElementClassInfo, options, writer, ref state); break; default: Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Unknown); // Treat typeof(object) as an empty object. finishedSerializing = WriteObject(options, writer, ref state); break; } if (flushThreshold >= 0 && writer.BytesPending > flushThreshold) { return(false); } if (finishedSerializing && writer.CurrentDepth == 0) { continueWriting = false; } } while (continueWriting); return(true); }
private static bool HandleObject( JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object); JsonPropertyInfo propertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex); state.Current.JsonPropertyInfo = propertyInfo; ClassType propertyClassType = propertyInfo.ClassType; if (propertyClassType == ClassType.Value) { propertyInfo.Write(options, ref state.Current, ref writer); state.Current.NextProperty(); return(true); } // A property that returns an enumerator keeps the same stack frame. if (propertyClassType == ClassType.Enumerable) { bool endOfEnumerable = HandleEnumerable(propertyInfo.ElementClassInfo, options, ref writer, ref state); if (endOfEnumerable) { state.Current.NextProperty(); } return(endOfEnumerable); } // A property that returns an object requires a new stack frame. object value = propertyInfo.GetValueAsObject(state.Current.CurrentValue, options); if (value != null) { JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo; state.Current.NextProperty(); JsonClassInfo nextClassInfo = options.GetOrAddClass(propertyInfo.PropertyType); state.Push(nextClassInfo, value); // Set the PropertyInfo so we can obtain the property name in order to write it. state.Current.JsonPropertyInfo = previousPropertyInfo; } else if (!propertyInfo.IgnoreNullPropertyValueOnWrite(options)) { writer.WriteNull(propertyInfo._escapedName); } return(true); }
public sealed override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { // Bridge from resumable to value converters. if (options == null) { throw new ArgumentNullException(nameof(options)); } WriteStack state = default; state.Initialize(typeof(T), options, supportContinuation: false); TryWrite(writer, value, options, ref state); }
private static async Task WriteAsyncCore(object value, Type type, Stream utf8Json, JsonSerializerOptions options, CancellationToken cancellationToken) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } JsonWriterOptions writerOptions = options.GetWriterOptions(); using (var bufferWriter = new PooledByteBufferWriter(options.DefaultBufferSize)) using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions)) { if (value == null) { writer.WriteNullValue(); writer.Flush(); await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); return; } if (type == null) { type = value.GetType(); } WriteStack state = default; state.Current.Initialize(type, options); state.Current.CurrentValue = value; bool isFinalBlock; int flushThreshold; do { flushThreshold = (int)(bufferWriter.Capacity * .9); //todo: determine best value here isFinalBlock = Write(writer, flushThreshold, options, ref state); writer.Flush(); await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); bufferWriter.Clear(); } while (!isFinalBlock); } // todo: verify that we do want to call FlushAsync here (or above). It seems like leaving it to the caller would be best. }
// There are three conditions to consider for an object (primitive value, enumerable or object) being processed here: // 1) The object type was specified as the root-level return type to a Parse\Read method. // 2) The object is property on a parent object. // 3) The object is an element in an enumerable. private static bool Write( ref Utf8JsonWriter writer, int flushThreshold, JsonSerializerOptions options, ref WriteStack state) { bool continueWriting = true; bool finishedSerializing; do { switch (state.Current.JsonClassInfo.ClassType) { case ClassType.Enumerable: finishedSerializing = WriteEnumerable(options, ref writer, ref state); break; case ClassType.Value: finishedSerializing = WriteValue(options, ref writer, ref state.Current); break; case ClassType.Object: finishedSerializing = WriteObject(options, ref writer, ref state); break; default: Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Unknown); // Treat typeof(object) as an empty object. finishedSerializing = WriteObject(options, ref writer, ref state); break; } if (flushThreshold >= 0 && writer.BytesWritten > flushThreshold) { return(false); } if (finishedSerializing && writer.CurrentDepth == 0) { continueWriting = false; } } while (continueWriting); return(true); }
private static bool Write( ref JsonWriterState writerState, IBufferWriter <byte> bufferWriter, int flushThreshold, JsonSerializerOptions options, ref WriteStack state) { Utf8JsonWriter writer = new Utf8JsonWriter(bufferWriter, writerState); bool isFinalBlock = Write( ref writer, flushThreshold, options, ref state); writer.Flush(isFinalBlock: isFinalBlock); writerState = writer.GetCurrentState(); return(isFinalBlock); }
private static bool WriteObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { JsonClassInfo classInfo = state.Current.JsonClassInfo; // Write the start. if (!state.Current.StartObjectWritten) { if (state.Current.JsonPropertyInfo?._escapedName == null) { writer.WriteStartObject(); } else { writer.WriteStartObject(state.Current.JsonPropertyInfo._escapedName); } state.Current.StartObjectWritten = true; } // Determine if we are done enumerating properties. if (state.Current.PropertyIndex != classInfo.PropertyCount) { HandleObject(options, writer, ref state); return(false); } writer.WriteEndObject(); if (state.Current.PopStackOnEndObject) { state.Pop(); } else { state.Current.EndObject(); } return(true); }
public sealed override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { if (options is null) { ThrowHelper.ThrowArgumentNullException(nameof(options)); } // Bridge from resumable to value converters. WriteStack state = default; JsonTypeInfo typeInfo = options.GetTypeInfoInternal(typeof(T)); state.Initialize(typeInfo); try { TryWrite(writer, value, options, ref state); } catch { state.DisposePendingDisposablesOnException(); throw; } }
protected internal abstract bool OnWriteResume(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOptions options, ref WriteStack state);
// 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)); }
// 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); }
internal bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state) { // If surpassed flush threshold then return false which will flush stream. return(state.FlushThreshold > 0 && writer.BytesPending > state.FlushThreshold); }
/// <summary> /// Loosely-typed WriteCore() that forwards to strongly-typed WriteCore(). /// </summary> internal abstract bool WriteCoreAsObject(Utf8JsonWriter writer, object?value, JsonSerializerOptions options, ref WriteStack state);
// Provide a default implementation for value converters. internal virtual bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state) { // TODO: https://github.com/dotnet/runtime/issues/32523 Write(writer, value !, options); return(true); }
/// <summary> /// Loosely-typed WriteToPropertyName() that forwards to strongly-typed WriteToPropertyName(). /// </summary> internal abstract void WriteAsPropertyNameAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state);
private static bool HandleDictionary( JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.Enumerator == null) { IEnumerable enumerable; enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); if (enumerable == null) { // Write a null object or enumerable. state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, writeNull: true); return(true); } state.Current.Enumerator = enumerable.GetEnumerator(); state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer); } if (state.Current.Enumerator.MoveNext()) { // Handle DataExtension. if (ReferenceEquals(jsonPropertyInfo, state.Current.JsonClassInfo.DataExtensionProperty)) { WriteExtensionData(writer, ref state.Current); } else { // Check for polymorphism. if (elementClassInfo.ClassType == ClassType.Unknown) { object currentValue = ((IDictionaryEnumerator)state.Current.Enumerator).Entry.Value; GetRuntimeClassInfo(currentValue, ref elementClassInfo, options); } if (elementClassInfo.ClassType == ClassType.Value) { elementClassInfo.GetPolicyProperty().WriteDictionary(ref state.Current, writer); } else if (state.Current.Enumerator.Current == null) { writer.WriteNull(jsonPropertyInfo.Name); } else { // An object or another enumerator requires a new stack frame. var enumerator = (IDictionaryEnumerator)state.Current.Enumerator; object value = enumerator.Value; state.Push(elementClassInfo, value); state.Current.KeyName = (string)enumerator.Key; } } return(false); } // We are done enumerating. writer.WriteEndObject(); if (state.Current.PopStackOnEnd) { state.Pop(); } else { state.Current.EndDictionary(); } return(true); }
private static bool HandleEnumerable( JsonClassInfo elementClassInfo, JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.Enumerator == null) { if (jsonPropertyInfo._name == null) { writer.WriteStartArray(); } else { writer.WriteStartArray(jsonPropertyInfo._name); } IEnumerable enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options); if (enumerable != null) { state.Current.Enumerator = enumerable.GetEnumerator(); } } if (state.Current.Enumerator != null && state.Current.Enumerator.MoveNext()) { // If the enumerator contains typeof(object), get the run-time type if (elementClassInfo.ClassType == ClassType.Object && jsonPropertyInfo.ElementClassInfo.Type == typeof(object)) { object currentValue = state.Current.Enumerator.Current; if (currentValue != null) { Type runtimeType = currentValue.GetType(); // Ignore object() instances since they are handled as an empty object. if (runtimeType != typeof(object)) { elementClassInfo = options.GetOrAddClass(runtimeType); } } } if (elementClassInfo.ClassType == ClassType.Value) { elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, ref writer); } else if (state.Current.Enumerator.Current == null) { // Write a null object or enumerable. writer.WriteNullValue(); } else { // An object or another enumerator requires a new stack frame. object nextValue = state.Current.Enumerator.Current; state.Push(elementClassInfo, nextValue); } return(false); } // We are done enumerating. writer.WriteEndArray(); if (state.Current.PopStackOnEndArray) { state.Pop(); } else { state.Current.EndArray(); } return(true); }
internal bool TryHandleSerializedObjectReference(Utf8JsonWriter writer, object value, JsonSerializerOptions options, JsonConverter?polymorphicConverter, ref WriteStack state) { Debug.Assert(!IsValueType); Debug.Assert(!state.IsContinuation); Debug.Assert(value != null); switch (options.ReferenceHandlingStrategy) { case ReferenceHandlingStrategy.IgnoreCycles: ReferenceResolver resolver = state.ReferenceResolver; if (resolver.ContainsReferenceForCycleDetection(value)) { writer.WriteNullValue(); return(true); } resolver.PushReferenceForCycleDetection(value); // WriteStack reuses root-level stackframes for its children as a performance optimization; // we want to avoid writing any data for the root-level object to avoid corrupting the stack. // This is fine since popping the root object at the end of serialization is not essential. state.Current.IsPushedReferenceForCycleDetection = state.CurrentDepth > 0; break; case ReferenceHandlingStrategy.Preserve: bool canHaveIdMetata = polymorphicConverter?.CanHaveMetadata ?? CanHaveMetadata; if (canHaveIdMetata && JsonSerializer.TryGetReferenceForValue(value, ref state, writer)) { // We found a repeating reference and wrote the relevant metadata; serialization complete. return(true); } break; default: Debug.Fail("Unexpected ReferenceHandlingStrategy."); break; } return(false); }
/// <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); }
internal bool TryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state) { if (writer.CurrentDepth >= options.EffectiveMaxDepth) { ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); } if (CanBePolymorphic) { if (value == null) { if (!HandleNull) { writer.WriteNullValue(); } else { Debug.Assert(ClassType == ClassType.Value); Debug.Assert(!state.IsContinuation); int originalPropertyDepth = writer.CurrentDepth; Write(writer, value, options); VerifyWrite(originalPropertyDepth, writer); } return(true); } Type type = value.GetType(); if (type == typeof(object)) { writer.WriteStartObject(); writer.WriteEndObject(); return(true); } if (type != TypeToConvert) { // Handle polymorphic case and get the new converter. JsonConverter jsonConverter = state.Current.InitializeReEntry(type, options); if (jsonConverter != this) { // We found a different converter; forward to that. return(jsonConverter.TryWriteAsObject(writer, value, options, ref state)); } } } else if (value == null && !HandleNull) { // We do not pass null values to converters unless HandleNull is true. Null values for properties were // already handled in GetMemberAndWriteJson() so we don't need to check for IgnoreNullValues here. writer.WriteNullValue(); return(true); } if (ClassType == ClassType.Value) { Debug.Assert(!state.IsContinuation); int originalPropertyDepth = writer.CurrentDepth; Write(writer, value, options); VerifyWrite(originalPropertyDepth, writer); return(true); } bool isContinuation = state.IsContinuation; state.Push(); if (!isContinuation) { Debug.Assert(state.Current.OriginalDepth == 0); state.Current.OriginalDepth = writer.CurrentDepth; } bool success = OnTryWrite(writer, value, options, ref state); if (success) { VerifyWrite(state.Current.OriginalDepth, writer); // No need to clear state.Current.OriginalDepth since a stack pop will occur. } state.Pop(success); return(success); }
internal bool TryWriteDataExtensionProperty(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state) { Debug.Assert(value != null); if (!IsInternalConverter) { return(TryWrite(writer, value, options, ref state)); } Debug.Assert(this is JsonDictionaryConverter <T>); state.Current.PolymorphicJsonPropertyInfo = state.Current.DeclaredJsonPropertyInfo !.RuntimeClassInfo.ElementClassInfo !.PropertyInfoForClassInfo; if (writer.CurrentDepth >= options.EffectiveMaxDepth) { ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); } JsonDictionaryConverter <T> dictionaryConverter = (JsonDictionaryConverter <T>) this; bool isContinuation = state.IsContinuation; bool success; state.Push(); if (!isContinuation) { Debug.Assert(state.Current.OriginalDepth == 0); state.Current.OriginalDepth = writer.CurrentDepth; } // Ignore the naming policy for extension data. state.Current.IgnoreDictionaryKeyPolicy = true; success = dictionaryConverter.OnWriteResume(writer, value, options, ref state); if (success) { VerifyWrite(state.Current.OriginalDepth, writer); } state.Pop(success); return(success); }
private static bool HandleEnumerable( JsonClassInfo elementClassInfo, JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo; if (state.Current.Enumerator == null) { if (jsonPropertyInfo._name == null) { writer.WriteStartArray(); } else { writer.WriteStartArray(jsonPropertyInfo._name); } IEnumerable enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options); if (enumerable != null) { state.Current.Enumerator = enumerable.GetEnumerator(); } } if (state.Current.Enumerator != null && state.Current.Enumerator.MoveNext()) { // Check for polymorphism. if (elementClassInfo.ClassType == ClassType.Unknown) { object currentValue = state.Current.Enumerator.Current; GetRuntimeClassInfo(currentValue, ref elementClassInfo, options); } if (elementClassInfo.ClassType == ClassType.Value) { elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, ref writer); } else if (state.Current.Enumerator.Current == null) { // Write a null object or enumerable. writer.WriteNullValue(); } else { // An object or another enumerator requires a new stack frame. object nextValue = state.Current.Enumerator.Current; state.Push(elementClassInfo, nextValue); } return(false); } // We are done enumerating. writer.WriteEndArray(); if (state.Current.PopStackOnEndArray) { state.Pop(); } else { state.Current.EndArray(); } return(true); }
private static bool HandleObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert( state.Current.JsonClassInfo.ClassType == ClassType.Object || state.Current.JsonClassInfo.ClassType == ClassType.Unknown); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex); bool obtainedValue = false; object currentValue = null; // Check for polymorphism. if (jsonPropertyInfo.ClassType == ClassType.Unknown) { currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); obtainedValue = true; GetRuntimePropertyInfo(currentValue, state.Current.JsonClassInfo, ref jsonPropertyInfo, options); } state.Current.JsonPropertyInfo = jsonPropertyInfo; if (jsonPropertyInfo.ClassType == ClassType.Value) { jsonPropertyInfo.Write(options, ref state.Current, writer); state.Current.NextProperty(); return(true); } // A property that returns an enumerator keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.Enumerable) { bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) { state.Current.NextProperty(); } return(endOfEnumerable); } // A property that returns a dictionary keeps the same stack frame. if (jsonPropertyInfo.ClassType == ClassType.Dictionary) { bool endOfEnumerable = HandleDictionary(jsonPropertyInfo.ElementClassInfo, options, writer, ref state); if (endOfEnumerable) { state.Current.NextProperty(); } return(endOfEnumerable); } // A property that returns an object. if (!obtainedValue) { currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); } if (currentValue != null) { // A new stack frame is required. JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo; state.Current.NextProperty(); JsonClassInfo nextClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); state.Push(nextClassInfo, currentValue); // Set the PropertyInfo so we can obtain the property name in order to write it. state.Current.JsonPropertyInfo = previousPropertyInfo; } else { if (!jsonPropertyInfo.IgnoreNullValues) { writer.WriteNull(jsonPropertyInfo._escapedName); } state.Current.NextProperty(); } return(true); }
/// <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); }
/// <summary> /// Loosely-typed WriteWithQuotes() that forwards to strongly-typed WriteWithQuotes(). /// </summary> internal abstract void WriteWithQuotesAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state);
private static bool HandleObject( JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteStack state) { Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex); ClassType propertyClassType = jsonPropertyInfo.ClassType; bool obtainedValue = false; object currentValue = null; // Check for polymorphism. if (jsonPropertyInfo.RuntimePropertyType == typeof(object)) { Debug.Assert(propertyClassType == ClassType.Object); currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options); obtainedValue = true; if (currentValue != null) { Type runtimeType = currentValue.GetType(); // Ignore object() instances since they are handled as an empty object. if (runtimeType != typeof(object)) { propertyClassType = JsonClassInfo.GetClassType(runtimeType); jsonPropertyInfo = state.Current.JsonClassInfo.CreatePolymorphicProperty(jsonPropertyInfo, runtimeType, options); } } } state.Current.JsonPropertyInfo = jsonPropertyInfo; if (propertyClassType == ClassType.Value) { jsonPropertyInfo.Write(options, ref state.Current, ref writer); state.Current.NextProperty(); return(true); } // A property that returns an enumerator keeps the same stack frame. if (propertyClassType == ClassType.Enumerable) { bool endOfEnumerable = HandleEnumerable(jsonPropertyInfo.ElementClassInfo, options, ref writer, ref state); if (endOfEnumerable) { state.Current.NextProperty(); } return(endOfEnumerable); } // A property that returns an object. if (!obtainedValue) { currentValue = jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue, options); } if (currentValue != null) { // A new stack frame is required. JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo; state.Current.NextProperty(); JsonClassInfo nextClassInfo = options.GetOrAddClass(jsonPropertyInfo.RuntimePropertyType); state.Push(nextClassInfo, currentValue); // Set the PropertyInfo so we can obtain the property name in order to write it. state.Current.JsonPropertyInfo = previousPropertyInfo; } else { if (!jsonPropertyInfo.IgnoreNullPropertyValueOnWrite(options)) { writer.WriteNull(jsonPropertyInfo._escapedName); } state.Current.NextProperty(); } return(true); }