예제 #1
0
        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));
 }
예제 #3
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
        }
예제 #9
0
        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;
            }
        }
예제 #10
0
        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);
        }
예제 #11
0
 protected internal abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteObjectState current, ref Utf8JsonWriter writer);
예제 #12
0
 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);
        }
예제 #14
0
        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}");
                }
            }
        }
예제 #15
0
        // 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}");
                        }
                    }
                }
            }
        }