public void WriteEnumerable(ref WriteStack state, Utf8JsonWriter writer) { Debug.Assert(ShouldSerialize); int originalDepth = writer.CurrentDepth; OnWriteEnumerable(ref state.Current, writer); VerifyWrite(originalDepth, ref state, ref writer); }
private static bool WriteEndObject(ref WriteStack state) { if (state.Current.PopStackOnEndObject) { state.Pop(); } return(true); }
public override bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer) { Debug.Assert(EscapedName.HasValue); bool success; T value = Get !(obj); if (value == null) { Debug.Assert(s_defaultValue == null && Converter.CanBeNull); success = true; if (!IgnoreDefaultValuesOnWrite) { if (!Converter.HandleNull) { writer.WriteNull(EscapedName.Value); } else { // No object, collection, or re-entrancy converter handles null. Debug.Assert(Converter.ClassType == ClassType.Value); if (state.Current.PropertyState < StackFramePropertyState.Name) { state.Current.PropertyState = StackFramePropertyState.Name; writer.WritePropertyName(EscapedName.Value); } int originalDepth = writer.CurrentDepth; Converter.Write(writer, value, Options); if (originalDepth != writer.CurrentDepth) { ThrowHelper.ThrowJsonException_SerializationConverterWrite(Converter); } } } } else if (IgnoreDefaultValuesOnWrite && Converter._defaultComparer.Equals(s_defaultValue, value)) { Debug.Assert(s_defaultValue != null && !Converter.CanBeNull); success = true; } else { if (state.Current.PropertyState < StackFramePropertyState.Name) { state.Current.PropertyState = StackFramePropertyState.Name; writer.WritePropertyName(EscapedName.Value); } success = Converter.TryWrite(writer, value, Options, ref state); } return(success); }
public override bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer) { T value = Get !(obj); if (Options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.IgnoreCycles && !Converter.IsValueType && value != null && state.ReferenceResolver.ContainsReferenceForCycleDetection(value)) { // If a reference cycle is detected, treat value as null. value = default !;
// 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 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: case ClassType.KeyValuePair: finishedSerializing = WriteObject(options, writer, ref state); break; case ClassType.Dictionary: case ClassType.IDictionaryConstructible: 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 (finishedSerializing && writer.CurrentDepth == 0) { break; } // If serialization is not yet end and we surpass beyond flush threshold return false and flush stream. if (flushThreshold >= 0 && writer.BytesPending > flushThreshold) { return(false); } } while (true); return(true); }
private static async Task WriteAsyncCore(Stream utf8Json, object?value, Type inputType, 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 (inputType == null) { inputType = value.GetType(); } WriteStack state = default; state.InitializeRoot(inputType, options, supportContinuation: true); bool isFinalBlock; do { // todo: determine best value here // https://github.com/dotnet/runtime/issues/32356 state.FlushThreshold = (int)(bufferWriter.Capacity * .9); isFinalBlock = WriteCore( writer, value, options, ref state, state.Current.JsonClassInfo !.PolicyProperty !.ConverterBase); 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. }
private static async Task WriteAsyncCore(Stream utf8Json, object?value, Type inputType, 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 (inputType == null) { inputType = value.GetType(); } WriteStack state = default; if (options.ReferenceHandling.ShouldWritePreservedReferences()) { state.ReferenceResolver = new DefaultReferenceResolver(writing: true); } state.Current.Initialize(inputType, options); state.Current.CurrentValue = value; bool isFinalBlock; int flushThreshold; do { flushThreshold = (int)(bufferWriter.Capacity * .9); //todo: determine best value here isFinalBlock = Write(writer, originalWriterDepth: 0, 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. }
private static bool WriteObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { // Write the start. if (!state.Current.StartObjectWritten) { state.Current.WriteObjectOrArrayStart(ClassType.Object, writer); state.Current.PropertyEnumerator = state.Current.JsonClassInfo.PropertyCache.GetEnumerator(); state.Current.PropertyEnumeratorActive = true; state.Current.NextProperty(); } else if (state.Current.MoveToNextProperty) { state.Current.NextProperty(); } // Determine if we are done enumerating properties. // If the ClassType is unknown, there will be a policy property applied JsonClassInfo classInfo = state.Current.JsonClassInfo; if (classInfo.ClassType != ClassType.Unknown && state.Current.PropertyEnumeratorActive) { var kvp = (KeyValuePair <string, JsonPropertyInfo>)state.Current.PropertyEnumerator.Current; JsonPropertyInfo jsonPropertyInfo = kvp.Value; HandleObject(jsonPropertyInfo, options, writer, ref state); return(false); } if (state.Current.ExtensionDataStatus == Serialization.ExtensionDataWriteStatus.Writing) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.DataExtensionProperty; if (jsonPropertyInfo != null) { HandleObject(jsonPropertyInfo, options, writer, ref state); return(false); } } writer.WriteEndObject(); if (state.Current.PopStackOnEndObject) { state.Pop(); } else { state.Current.EndObject(); } return(true); }
internal static MetadataPropertyName WriteMetadataForCollection( JsonConverter jsonConverter, ref WriteStack state, Utf8JsonWriter writer) { // For collections with metadata, we nest the array payload within a JSON object. writer.WriteStartObject(); MetadataPropertyName writtenMetadata = WriteMetadataForObject(jsonConverter, ref state, writer); writer.WritePropertyName(s_metadataValues); // property name containing nested array values. return(writtenMetadata); }
public void WriteEnumerable(ref WriteStack state, Utf8JsonWriter writer) { Debug.Assert(ShouldSerialize); int originalDepth = writer.CurrentDepth; OnWriteEnumerable(ref state.Current, writer); if (originalDepth != writer.CurrentDepth) { ThrowHelper.ThrowJsonException_SerializationConverterWrite(state.PropertyPath(), ConverterBase.ToString()); } }
private static bool WriteEndDictionary(ref WriteStack state) { if (state.Current.PopStackOnEndCollection) { state.Pop(); } else { state.Current.EndDictionary(); } return(true); }
public override bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer) { T value = Get !(obj); // Since devirtualization only works in non-shared generics, // the default comparer is uded only for value types for now. // For reference types there is a quick check for null. if (default(T) == null && IgnoreDefaultValuesForReferenceTypesOnWrite && value == null) { return(true); } if (default(T) != null && IgnoreDefaultValuesForValueTypesOnWrite && EqualityComparer <T> .Default.Equals(default, value))
private static async Task WriteAsyncCore <TValue>( Stream utf8Json, TValue value, Type inputType, JsonSerializerOptions?options, CancellationToken cancellationToken) { // We flush the Stream when the buffer is >=90% of capacity. // This threshold is a compromise between buffer utilization and minimizing cases where the buffer // needs to be expanded\doubled because it is not large enough to write the current property or element. // We check for flush after each object property and array element is written to the buffer. // Once the buffer is expanded to contain the largest single element\property, a 90% thresold // means the buffer may be expanded a maximum of 4 times: 1-(1\(2^4))==.9375. const float FlushThreshold = .9f; 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)) { // We treat typeof(object) special and allow polymorphic behavior. if (inputType == typeof(object) && value != null) { inputType = value !.GetType(); } WriteStack state = default; state.Initialize(inputType, options, supportContinuation: true); JsonConverter converterBase = state.Current.JsonClassInfo !.PropertyInfoForClassInfo.ConverterBase; bool isFinalBlock; do { state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold); isFinalBlock = WriteCore(converterBase, writer, value, options, ref state); await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false); bufferWriter.Clear(); } while (!isFinalBlock); } }
internal static MetadataPropertyName WriteReferenceForCollection( JsonConverter jsonConverter, object currentValue, ref WriteStack state, Utf8JsonWriter writer) { MetadataPropertyName writtenMetadataName; if (state.BoxedStructReferenceId != null) { // We're serializing a struct that has been handled by a polymorphic converter; // emit the reference id that was recorded for the boxed instance. Debug.Assert(jsonConverter.IsValueType && jsonConverter.CanHaveIdMetadata); writer.WriteStartObject(); writer.WriteString(s_metadataId, state.BoxedStructReferenceId); writer.WriteStartArray(s_metadataValues); writtenMetadataName = MetadataPropertyName.Id; state.BoxedStructReferenceId = null; } else if (!jsonConverter.CanHaveIdMetadata || jsonConverter.IsValueType) { // If the jsonConverter supports immutable enumerables or value type collections, don't write any metadata writer.WriteStartArray(); writtenMetadataName = MetadataPropertyName.NoMetadata; } else { string referenceId = state.ReferenceResolver.GetReference(currentValue, out bool alreadyExists); Debug.Assert(referenceId != null); if (alreadyExists) { writer.WriteStartObject(); writer.WriteString(s_metadataRef, referenceId); writer.WriteEndObject(); writtenMetadataName = MetadataPropertyName.Ref; } else { writer.WriteStartObject(); writer.WriteString(s_metadataId, referenceId); writer.WriteStartArray(s_metadataValues); writtenMetadataName = MetadataPropertyName.Id; } } return(writtenMetadataName); }
private static bool WriteObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { // Write the start. if (!state.Current.StartObjectWritten) { // If true, we are writing a root object or a value that doesn't belong // to an object e.g. a dictionary value. if (state.Current.CurrentValue == null) { state.Current.WriteObjectOrArrayStart(ClassType.Object, writer, options, writeNull: true); return(WriteEndObject(ref state)); } state.Current.WriteObjectOrArrayStart(ClassType.Object, writer, options); state.Current.PropertyEnumerator = state.Current.JsonClassInfo.PropertyCache.GetEnumerator(); state.Current.PropertyEnumeratorActive = true; state.Current.NextProperty(); } else if (state.Current.MoveToNextProperty) { state.Current.NextProperty(); } // Determine if we are done enumerating properties. // If the ClassType is unknown, there will be a policy property applied JsonClassInfo classInfo = state.Current.JsonClassInfo; if (classInfo.ClassType != ClassType.Unknown && state.Current.PropertyEnumeratorActive) { HandleObject(state.Current.PropertyEnumerator.Current.Value, options, writer, ref state); return(false); } if (state.Current.ExtensionDataStatus == Serialization.ExtensionDataWriteStatus.Writing) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.DataExtensionProperty; if (jsonPropertyInfo != null) { HandleObject(jsonPropertyInfo, options, writer, ref state); return(false); } } writer.WriteEndObject(); return(WriteEndObject(ref state)); }
internal static MetadataPropertyName WriteReferenceForObject( JsonConverter jsonConverter, ref WriteStack state, Utf8JsonWriter writer) { if (state.NewReferenceId != null) { Debug.Assert(jsonConverter.CanHaveMetadata); writer.WriteString(s_metadataId, state.NewReferenceId); state.NewReferenceId = null; return(MetadataPropertyName.Id); } return(MetadataPropertyName.None); }
public override bool GetMemberAndWriteJsonExtensionData(object obj, ref WriteStack state, Utf8JsonWriter writer) { bool success; T value = Get !(obj); if (value == null) { success = true; } else { success = Converter.TryWriteDataExtensionProperty(writer, value, Options, ref state); } return(success); }
public override bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer) { Debug.Assert(EscapedName.HasValue); bool success; T value = Get !(obj); if (value == null) { if (!IgnoreNullValues) { if (!Converter.HandleNull) { writer.WriteNull(EscapedName.Value); } else { if (state.Current.PropertyState < StackFramePropertyState.Name) { state.Current.PropertyState = StackFramePropertyState.Name; writer.WritePropertyName(EscapedName.Value); } int originalDepth = writer.CurrentDepth; Converter.Write(writer, value, Options); if (originalDepth != writer.CurrentDepth) { ThrowHelper.ThrowJsonException_SerializationConverterWrite(Converter); } } } success = true; } else { if (state.Current.PropertyState < StackFramePropertyState.Name) { state.Current.PropertyState = StackFramePropertyState.Name; writer.WritePropertyName(EscapedName.Value); } success = Converter.TryWrite(writer, value, Options, ref state); } return(success); }
public override bool GetMemberAndWriteJsonExtensionData(object obj, ref WriteStack state, Utf8JsonWriter writer) { bool success; TTypeToConvert value = Get !(obj); if (value == null) { success = true; } else { state.Current.PolymorphicJsonPropertyInfo = state.Current.DeclaredJsonPropertyInfo !.RuntimeClassInfo.ElementClassInfo !.PropertyInfoForClassInfo; success = Converter.TryWriteDataExtensionProperty(writer, value, Options, ref state); } return(success); }
public void Write(ref WriteStack state, Utf8JsonWriter writer) { Debug.Assert(ShouldSerialize); if (state.Current.Enumerator != null) { // Forward the setter to the value-based JsonPropertyInfo. JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty(); propertyInfo.OnWriteEnumerable(ref state.Current, writer); } else { int originalDepth = writer.CurrentDepth; OnWrite(ref state.Current, writer); VerifyWrite(originalDepth, ref state, ref writer); } }
public override bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer) { T value = Get !(obj); if (IgnoreDefaultValuesOnWrite) { // If value is null, it is a reference type or nullable<T>. if (value == null) { return(true); } if (!PropertyTypeCanBeNull) { if (_propertyTypeEqualsTypeToConvert) { // The converter and property types are the same, so we can use T for EqualityComparer<>. if (EqualityComparer <T> .Default.Equals(default, value))
internal static MetadataPropertyName WriteMetadataForObject( JsonConverter jsonConverter, ref WriteStack state, Utf8JsonWriter writer) { Debug.Assert(jsonConverter.CanHaveMetadata); Debug.Assert(!state.IsContinuation); Debug.Assert(state.CurrentContainsMetadata); MetadataPropertyName writtenMetadata = MetadataPropertyName.None; if (state.NewReferenceId != null) { writer.WriteString(s_metadataId, state.NewReferenceId); writtenMetadata |= MetadataPropertyName.Id; state.NewReferenceId = null; } if (state.PolymorphicTypeDiscriminator is object discriminator) { Debug.Assert(state.Parent.JsonPropertyInfo !.JsonTypeInfo.PolymorphicTypeResolver != null); JsonEncodedText propertyName = state.Parent.JsonPropertyInfo.JsonTypeInfo.PolymorphicTypeResolver.CustomTypeDiscriminatorPropertyNameJsonEncoded is JsonEncodedText customPropertyName ? customPropertyName : s_metadataType; if (discriminator is string stringId) { writer.WriteString(propertyName, stringId); } else { Debug.Assert(discriminator is int); writer.WriteNumber(propertyName, (int)discriminator); } writtenMetadata |= MetadataPropertyName.Type; state.PolymorphicTypeDiscriminator = null; } Debug.Assert(writtenMetadata != MetadataPropertyName.None); return(writtenMetadata); }
private static void WriteCore <TValue>(Utf8JsonWriter writer, TValue value, Type inputType, JsonSerializerOptions options) { Debug.Assert(writer != null); // We treat typeof(object) special and allow polymorphic behavior. if (inputType == typeof(object) && value != null) { inputType = value !.GetType(); } WriteStack state = default; state.Initialize(inputType, options, supportContinuation: false); JsonConverter jsonConverter = state.Current.JsonClassInfo !.PropertyInfoForClassInfo.ConverterBase; bool success = WriteCore(jsonConverter, writer, value, options, ref state); Debug.Assert(success); }
internal static MetadataPropertyName WriteReferenceForCollection( JsonConverter jsonConverter, ref WriteStack state, Utf8JsonWriter writer) { if (state.NewReferenceId != null) { Debug.Assert(jsonConverter.CanHaveMetadata); writer.WriteStartObject(); writer.WriteString(s_metadataId, state.NewReferenceId); writer.WriteStartArray(s_metadataValues); state.NewReferenceId = null; return(MetadataPropertyName.Id); } // If the jsonConverter supports immutable enumerables or value type collections, don't write any metadata writer.WriteStartArray(); return(MetadataPropertyName.None); }
private static bool WriteObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { // Write the start. if (!state.Current.StartObjectWritten) { // If true, we are writing a root object or a value that doesn't belong // to an object e.g. a dictionary value. if (state.Current.CurrentValue == null) { state.Current.WriteObjectOrArrayStart(ClassType.Object, writer, options, writeNull: true); return(WriteEndObject(ref state)); } if (options.ReferenceHandling.ShouldWritePreservedReferences()) { if (WriteReference(ref state, writer, options, ClassType.Object, state.Current.CurrentValue)) { return(WriteEndObject(ref state)); } } else { state.Current.WriteObjectOrArrayStart(ClassType.Object, writer, options); } state.Current.MoveToNextProperty = true; } if (state.Current.MoveToNextProperty) { state.Current.NextProperty(); } // Determine if we are done enumerating properties. if (state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Finished) { // If ClassType.Unknown at this point, we are typeof(object) which should not have any properties. Debug.Assert(state.Current.JsonClassInfo !.ClassType != ClassType.Unknown); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.PropertyCacheArray ![state.Current.PropertyEnumeratorIndex - 1];
internal static MetadataPropertyName GetResolvedReferenceHandling( JsonConverter converter, object value, ref WriteStack state, out string?referenceId) { if (!converter.CanHaveIdMetadata || converter.TypeToConvert.IsValueType) { referenceId = default; return(MetadataPropertyName.NoMetadata); } if (state.ReferenceResolver.TryGetOrAddReferenceOnSerialize(value, out referenceId)) { return(MetadataPropertyName.Ref); } return(MetadataPropertyName.Id); }
internal static MetadataPropertyName WriteReferenceForObject( JsonConverter jsonConverter, object currentValue, ref WriteStack state, Utf8JsonWriter writer) { MetadataPropertyName metadataToWrite = GetResolvedReferenceHandling(jsonConverter, currentValue, ref state, out string?referenceId); if (metadataToWrite == MetadataPropertyName.Ref) { writer.WriteString(s_metadataRef, referenceId !); writer.WriteEndObject(); } else if (metadataToWrite == MetadataPropertyName.Id) { writer.WriteString(s_metadataId, referenceId !); } return(metadataToWrite); }
private static async Task WriteAsyncCore <TValue>(Stream utf8Json, TValue value, Type inputType, 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)) { // We treat typeof(object) special and allow polymorphic behavior. if (inputType == typeof(object) && value != null) { inputType = value !.GetType(); } WriteStack state = default; state.Initialize(inputType, options, supportContinuation: true); JsonConverter converterBase = state.Current.JsonClassInfo !.PropertyInfoForClassInfo.ConverterBase; bool isFinalBlock; do { // todo: determine best value here // https://github.com/dotnet/runtime/issues/32356 state.FlushThreshold = (int)(bufferWriter.Capacity * .9); isFinalBlock = WriteCore(converterBase, writer, value, options, ref state); 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. }
private static bool WriteObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { // Write the start. if (!state.Current.StartObjectWritten) { // If true, we are writing a root object or a value that doesn't belong // to an object e.g. a dictionary value. if (state.Current.CurrentValue == null) { state.Current.WriteObjectOrArrayStart(ClassType.Object, writer, options, writeNull: true); return(WriteEndObject(ref state)); } state.Current.WriteObjectOrArrayStart(ClassType.Object, writer, options); state.Current.PropertyEnumeratorActive = true; state.Current.MoveToNextProperty = true; } if (state.Current.MoveToNextProperty) { state.Current.NextProperty(); } // Determine if we are done enumerating properties. if (state.Current.PropertyEnumeratorActive) { // If ClassType.Unknown at this point, we are typeof(object) which should not have any properties. Debug.Assert(state.Current.JsonClassInfo.ClassType != ClassType.Unknown); JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.PropertyCacheArray[state.Current.PropertyEnumeratorIndex - 1]; HandleObject(jsonPropertyInfo, options, writer, ref state); return(false); } writer.WriteEndObject(); return(WriteEndObject(ref state)); }
private static bool WriteObject( JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { // Write the start. if (!state.Current.StartObjectWritten) { state.Current.WriteObjectOrArrayStart(ClassType.Object, writer); } if (state.Current.MoveToNextProperty) { state.Current.NextProperty(); } // Determine if we are done enumerating properties. // If the ClassType is unknown, there will be a policy property applied. There is probably // a better way to identify policy properties- maybe not put them in the normal property bag? JsonClassInfo classInfo = state.Current.JsonClassInfo; if (classInfo.ClassType != ClassType.Unknown && 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); }