private static async Task WriteAsyncInternal(object value, Type type, Stream utf8Stream, JsonSerializerOptions options, CancellationToken cancellationToken) { if (options == null) { options = s_defaultSettings; } var writerState = new JsonWriterState(options.WriterOptions); using (var bufferWriter = new ArrayBufferWriter <byte>(options.EffectiveBufferSize)) { if (value == null) { WriteNull(ref writerState, bufferWriter); #if BUILDING_INBOX_LIBRARY await utf8Stream.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false); #else // todo: stackalloc? await utf8Stream.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false); #endif return; } if (type == null) { type = value.GetType(); } JsonClassInfo classInfo = options.GetOrAddClass(type); WriteObjectState current = default; current.ClassInfo = classInfo; current.CurrentValue = value; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } List <WriteObjectState> previous = null; int arrayIndex = 0; bool isFinalBlock; int flushThreshold; do { flushThreshold = (int)(bufferWriter.Capacity * .9); //todo: determine best value here isFinalBlock = Write(ref writerState, bufferWriter, flushThreshold, options, ref current, ref previous, ref arrayIndex); #if BUILDING_INBOX_LIBRARY await utf8Stream.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false); #else // todo: stackalloc? await utf8Stream.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false); #endif bufferWriter.Clear(); } while (!isFinalBlock); } // todo: do we want to call FlushAsync here (or above)? It seems like leaving it to the caller would be better. //await stream.FlushAsync(cancellationToken).ConfigureAwait(false); }
private static bool WriteEnumerable( JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { return(HandleEnumerable(current.ClassInfo.ElementClassInfo, options, ref writer, ref current, ref previous, ref arrayIndex)); }
private static bool HandleObject( JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { Debug.Assert(current.ClassInfo.ClassType == ClassType.Object); JsonPropertyInfo propertyInfo = current.ClassInfo.GetProperty(current.PropertyIndex); current.PropertyInfo = propertyInfo; ClassType propertyClassType = propertyInfo.ClassType; if (propertyClassType == ClassType.Value) { propertyInfo.Write(options, ref current, ref writer); 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 current, ref previous, ref arrayIndex); if (endOfEnumerable) { current.NextProperty(); } return(endOfEnumerable); } // A property that returns an object requires a new stack frame. object value = propertyInfo.GetValueAsObject(current.CurrentValue, options); if (value != null) { JsonPropertyInfo previousPropertyInfo = current.PropertyInfo; current.NextProperty(); JsonClassInfo nextClassInfo = options.GetOrAddClass(propertyInfo.PropertyType); AddNewStackFrame(nextClassInfo, value, ref current, ref previous, ref arrayIndex); // Set the PropertyInfo so we can obtain the property name in order to write it. current.PropertyInfo = previousPropertyInfo; } else if (!propertyInfo.IgnoreNullPropertyValueOnWrite(options)) { writer.WriteNull(propertyInfo.Name); } return(true); }
private static bool WriteValue( JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteObjectState current) { Debug.Assert(current.PropertyInfo.ClassType == ClassType.Value); current.PropertyInfo.Write(options, ref current, ref writer); return(true); }
private static async Task WriteAsyncInternal(object value, Type type, PipeWriter utf8Writer, JsonSerializerOptions options, CancellationToken cancellationToken) { if (options == null) { options = s_defaultSettings; } var writerState = new JsonWriterState(options.WriterOptions); // Allocate the initial buffer. We don't want to use the existing buffer as there may be very few bytes left // and we won't be able to calculate flushThreshold appropriately. Memory <byte> memory = utf8Writer.GetMemory(options.EffectiveBufferSize); if (value == null) { WriteNull(ref writerState, utf8Writer); await utf8Writer.FlushAsync(cancellationToken).ConfigureAwait(false); return; } if (type == null) { type = value.GetType(); } JsonClassInfo classInfo = options.GetOrAddClass(type); WriteObjectState current = default; current.ClassInfo = classInfo; current.CurrentValue = value; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } List <WriteObjectState> previous = null; int arrayIndex = 0; bool isFinalBlock; // For Pipes there is not a way to get current buffer total size, so we just use the initial memory size. int flushThreshold = (int)(memory.Length * .9); //todo: determine best value here (extensible?) do { isFinalBlock = Write(ref writerState, utf8Writer, flushThreshold, options, ref current, ref previous, ref arrayIndex); await utf8Writer.FlushAsync(cancellationToken).ConfigureAwait(false); } while (!isFinalBlock); }
private static byte[] WriteInternal(object value, Type type, JsonSerializerOptions options) { if (options == null) { options = s_defaultSettings; } byte[] result; var state = new JsonWriterState(options.WriterOptions); using (var output = new ArrayBufferWriter <byte>(options.EffectiveBufferSize)) { var writer = new Utf8JsonWriter(output, state); if (value == null) { writer.WriteNullValue(); } else { if (type == null) { type = value.GetType(); } WriteObjectState current = default; JsonClassInfo classInfo = options.GetOrAddClass(type); current.ClassInfo = classInfo; current.CurrentValue = value; if (classInfo.ClassType != ClassType.Object) { current.PropertyInfo = classInfo.GetPolicyProperty(); } List <WriteObjectState> previous = null; int arrayIndex = 0; Write(ref writer, -1, options, ref current, ref previous, ref arrayIndex); } writer.Flush(isFinalBlock: true); result = output.WrittenMemory.ToArray(); } return(result); }
private static bool WriteObject( JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { // Write the start. if (!current.StartObjectWritten) { if (current.PropertyInfo?.Name == null) { writer.WriteStartObject(); } else { writer.WriteStartObject(current.PropertyInfo.Name); } current.StartObjectWritten = true; } // Determine if we are done enumerating properties. if (current.PropertyIndex != current.ClassInfo.PropertyCount) { HandleObject(options, ref writer, ref current, ref previous, ref arrayIndex); return(false); } // We are done enumerating properties. writer.WriteEndObject(); if (current.PopStackOnEndObject) { WriteObjectState previousFrame = default; GetPreviousState(ref previous, ref previousFrame, --arrayIndex); current = previousFrame; } else { current.EndObject(); } return(true); }
private static bool Write( ref Utf8JsonWriter writer, int flushThreshold, JsonSerializerOptions options, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { bool continueWriting = true; bool finishedSerializing; do { switch (current.ClassInfo.ClassType) { case ClassType.Enumerable: finishedSerializing = WriteEnumerable(options, ref writer, ref current, ref previous, ref arrayIndex); break; case ClassType.Object: finishedSerializing = WriteObject(options, ref writer, ref current, ref previous, ref arrayIndex); break; default: finishedSerializing = WriteValue(options, ref writer, ref current); break; } if (flushThreshold >= 0 && writer.BytesWritten > flushThreshold) { return(false); } if (finishedSerializing && writer.CurrentDepth == 0) { continueWriting = false; } } while (continueWriting); return(true); }
private static void AddNewStackFrame( JsonClassInfo nextClassInfo, object nextValue, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { SetPreviousState(ref previous, current, arrayIndex++); current.Reset(); current.ClassInfo = nextClassInfo; current.CurrentValue = nextValue; if (nextClassInfo.ClassType == ClassType.Enumerable) { current.PopStackOnEndArray = true; current.PropertyInfo = current.ClassInfo.GetPolicyProperty(); } else { Debug.Assert(nextClassInfo.ClassType == ClassType.Object); current.PopStackOnEndObject = true; } }
private static bool Write( ref JsonWriterState writerState, IBufferWriter <byte> bufferWriter, int flushThreshold, JsonSerializerOptions options, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { Utf8JsonWriter writer = new Utf8JsonWriter(bufferWriter, writerState); bool isFinalBlock = Write( ref writer, flushThreshold, options, ref current, ref previous, ref arrayIndex); writer.Flush(isFinalBlock: isFinalBlock); writerState = writer.GetCurrentState(); return(isFinalBlock); }
protected internal abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteObjectState current, ref Utf8JsonWriter writer);
public abstract void Write(JsonSerializerOptions options, ref WriteObjectState current, ref Utf8JsonWriter writer);
private static bool HandleEnumerable( JsonClassInfo elementClassInfo, JsonSerializerOptions options, ref Utf8JsonWriter writer, ref WriteObjectState current, ref List <WriteObjectState> previous, ref int arrayIndex) { Debug.Assert(current.PropertyInfo.ClassType == ClassType.Enumerable); JsonPropertyInfo propertyInfo = current.PropertyInfo; if (current.Enumerator == null) { if (propertyInfo.Name == null) { writer.WriteStartArray(); } else { writer.WriteStartArray(propertyInfo.Name); } IEnumerable enumerable = (IEnumerable)propertyInfo.GetValueAsObject(current.CurrentValue, options); if (enumerable != null) { current.Enumerator = enumerable.GetEnumerator(); } } if (current.Enumerator != null && current.Enumerator.MoveNext()) { if (elementClassInfo.ClassType == ClassType.Value) { propertyInfo.Write(options, ref current, ref writer); } else { // An object or another enumerator requires a new stack frame JsonClassInfo nextClassInfo = propertyInfo.ElementClassInfo; object nextValue = current.Enumerator.Current; AddNewStackFrame(nextClassInfo, nextValue, ref current, ref previous, ref arrayIndex); } return(false); } // We are done enumerating. writer.WriteEndArray(); if (current.PopStackOnEndArray) { WriteObjectState previousFrame = default; GetPreviousState(ref previous, ref previousFrame, --arrayIndex); current = previousFrame; } else { current.EndArray(); } return(true); }
protected internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteObjectState current, ref Utf8JsonWriter writer) { if (ValueConverter != null) { Debug.Assert(current.Enumerator != null); object value = current.Enumerator.Current; if (value == null) { writer.WriteNull(Name); } else { ValueConverter.SetWrite(ref writer, Name, value); } } else { if (this is IJsonSerializerInternal <TValue> converter) { Debug.Assert(current.Enumerator != null); TValue value = (TValue)current.Enumerator.Current; if (value == null) { writer.WriteNullValue(); } else { converter.Write(ref writer, value); } } else { throw new InvalidOperationException($"todo: there is no converter for {PropertyType}"); } } }
// todo: have the caller check if current.Enumerator != null and call WriteEnumerable of the underlying property directly to avoid an extra virtual call. public override void Write(JsonSerializerOptions options, ref WriteObjectState current, ref Utf8JsonWriter writer) { if (current.Enumerator != null) { // Forward the setter to the value-based JsonPropertyInfo. JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty(); propertyInfo.WriteEnumerable(options, ref current, ref writer); } else { if (ValueConverter != null) { TValue value = Get(current.CurrentValue); if (value == null) { if (Name == null) { writer.WriteNullValue(); } else if (!SkipNullValuesOnWrite(options)) { writer.WriteNull(Name); } } else { ValueConverter.SetWrite(ref writer, Name, value); } } else { TValue value = Get(current.CurrentValue); if (value == null) { if (Name == null) { writer.WriteNullValue(); } else if (!SkipNullValuesOnWrite(options)) { writer.WriteNull(Name); } } else { if (this is IJsonSerializerInternal <TValue> converter) { if (Name == null) { converter.Write(ref writer, value); } else if (!SkipNullValuesOnWrite(options)) { converter.Write(ref writer, Name, value); } } else { throw new InvalidOperationException($"todo: there is no converter for {PropertyType}"); } } } } }