Пример #1
0
        /// <summary>
        /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="utf8Json"/>.
        /// </summary>
        /// <param name="utf8Json">An instance of <see cref="Stream" /> used as a destination for writing JSON text into.</param>
        /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>
        /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (i.e. with no extra whitespace)
        /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the instance of <see cref="Stream" /> that is passed in is null.
        /// </exception>
        public Utf8JsonWriter(Stream utf8Json, JsonWriterOptions options = default)
        {
            if (utf8Json == null)
            {
                throw new ArgumentNullException(nameof(utf8Json));
            }
            if (!utf8Json.CanWrite)
            {
                throw new ArgumentException(SR.StreamNotWritable);
            }

            _stream            = utf8Json;
            _arrayBufferWriter = new ArrayBufferWriter <byte>();
            _output            = default;

            BytesPending   = default;
            BytesCommitted = default;
            _memory        = default;

            _inObject       = default;
            _isNotPrimitive = default;
            _tokenType      = default;
            _currentDepth   = default;
            Options         = options;

            // Only allocate if the user writes a JSON payload beyond the depth that the _allocationFreeContainer can handle.
            // This way we avoid allocations in the common, default cases, and allocate lazily.
            _bitStack = default;
        }
Пример #2
0
        /// <summary>
        /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="bufferWriter"/>.
        /// </summary>
        /// <param name="bufferWriter">An instance of <see cref="IBufferWriter{Byte}" /> used as a destination for writing JSON text into.</param>
        /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>
        /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (that is, with no extra whitespace)
        /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the instance of <see cref="IBufferWriter{Byte}" /> that is passed in is null.
        /// </exception>
        public Utf8JsonWriter(IBufferWriter <byte> bufferWriter, JsonWriterOptions options = default)
        {
            _output  = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter));
            _options = options;

            if (_options.MaxDepth == 0)
            {
                _options.MaxDepth = JsonWriterOptions.DefaultMaxDepth; // If max depth is not set, revert to the default depth.
            }
        }
        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.
        }
Пример #4
0
        internal void Reset(IBufferWriter <byte> bufferWriter, JsonWriterOptions options)
        {
            Debug.Assert(_output is null && _stream is null && _arrayBufferWriter is null);

            _output  = bufferWriter;
            _options = options;
            if (_options.MaxDepth == 0)
            {
                _options.MaxDepth = JsonWriterOptions.DefaultMaxDepth; // If max depth is not set, revert to the default depth.
            }
        }
Пример #5
0
        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.
        }
Пример #6
0
        /// <summary>
        /// Gets the json representation as string.
        /// </summary>
        /// <param name="json">The j.</param>
        /// <param name="options">The options.</param>
        /// <returns></returns>
        public static string AsString(
            this JsonElement json,
            JsonWriterOptions options = default)
        {
            using var ms = new MemoryStream();
            using (var w = new Utf8JsonWriter(ms, options))
            {
                json.WriteTo(w);
            }
            var result = Encoding.UTF8.GetString(ms.ToArray());

            return(result);
        }
Пример #7
0
        /// <summary>
        /// Gets the json representation as string.
        /// </summary>
        /// <param name="json">The j.</param>
        /// <param name="options">The options.</param>
        /// <returns></returns>
        public static Stream ToStream(
            this JsonElement json,
            JsonWriterOptions options = default)
        {
            var ms = new MemoryStream();

            using (var w = new Utf8JsonWriter(ms, options))
            {
                json.WriteTo(w);
            }
            ms.Seek(0, SeekOrigin.Begin);
            return(ms);
        }
Пример #8
0
        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);
                }
        }
Пример #9
0
        /// <summary>
        /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="utf8Json"/>.
        /// </summary>
        /// <param name="utf8Json">An instance of <see cref="Stream" /> used as a destination for writing JSON text into.</param>
        /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>
        /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (that is, with no extra whitespace)
        /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the instance of <see cref="Stream" /> that is passed in is null.
        /// </exception>
        public Utf8JsonWriter(Stream utf8Json, JsonWriterOptions options = default)
        {
            if (utf8Json == null)
            {
                throw new ArgumentNullException(nameof(utf8Json));
            }
            if (!utf8Json.CanWrite)
            {
                throw new ArgumentException(SR.StreamNotWritable);
            }

            _stream            = utf8Json;
            _options           = options;
            _arrayBufferWriter = new ArrayBufferWriter <byte>();
        }
Пример #10
0
        /// <summary>
        /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="bufferWriter"/>.
        /// </summary>
        /// <param name="bufferWriter">An instance of <see cref="IBufferWriter{Byte}" /> used as a destination for writing JSON text into.</param>
        /// <param name="state">If this is the first call to the ctor, pass in a default state. Otherwise,
        /// capture the state from the previous instance of the <see cref="Utf8JsonWriter"/> and pass that back.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the instance of <see cref="IBufferWriter{Byte}" /> that is passed in is null.
        /// </exception>
        /// <remarks>
        /// Since this type is a ref struct, it is a stack-only type and all the limitations of ref structs apply to it.
        /// This is the reason why the ctor accepts a <see cref="JsonWriterState"/>.
        /// </remarks>
        public Utf8JsonWriter(IBufferWriter <byte> bufferWriter, JsonWriterState state = default)
        {
            _output        = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter));
            _buffered      = 0;
            BytesCommitted = 0;
            _buffer        = _output.GetSpan();

            _inObject       = state._inObject;
            _isNotPrimitive = state._isNotPrimitive;
            _tokenType      = state._tokenType;
            _writerOptions  = state._writerOptions;
            _bitStack       = state._bitStack;

            _currentDepth = state._currentDepth;
        }
Пример #11
0
        /// <summary>
        /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="bufferWriter"/>.
        /// </summary>
        /// <param name="bufferWriter">An instance of <see cref="IBufferWriter{Byte}" /> used as a destination for writing JSON text into.</param>
        /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>
        /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (that is, with no extra whitespace)
        /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the instance of <see cref="IBufferWriter{Byte}" /> that is passed in is null.
        /// </exception>
        public Utf8JsonWriter(IBufferWriter <byte> bufferWriter, JsonWriterOptions options = default)
        {
            _output            = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter));
            _stream            = default;
            _arrayBufferWriter = default;

            BytesPending   = default;
            BytesCommitted = default;
            _memory        = default;

            _inObject     = default;
            _tokenType    = default;
            _currentDepth = default;
            _options      = options;

            // Only allocate if the user writes a JSON payload beyond the depth that the _allocationFreeContainer can handle.
            // This way we avoid allocations in the common, default cases, and allocate lazily.
            _bitStack = default;
        }
        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.
        }
Пример #13
0
        /// <summary>
        /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="utf8Json"/>.
        /// </summary>
        /// <param name="utf8Json">An instance of <see cref="Stream" /> used as a destination for writing JSON text into.</param>
        /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>
        /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (that is, with no extra whitespace)
        /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when the instance of <see cref="Stream" /> that is passed in is null.
        /// </exception>
        public Utf8JsonWriter(Stream utf8Json, JsonWriterOptions options = default)
        {
            if (utf8Json == null)
            {
                throw new ArgumentNullException(nameof(utf8Json));
            }
            if (!utf8Json.CanWrite)
            {
                throw new ArgumentException(SR.StreamNotWritable);
            }

            _stream  = utf8Json;
            _options = options;

            if (_options.MaxDepth == 0)
            {
                _options.MaxDepth = JsonWriterOptions.DefaultMaxDepth; // If max depth is not set, revert to the default depth.
            }

            _arrayBufferWriter = new ArrayBufferWriter <byte>();
        }
Пример #14
0
        public static string SerializeWithContextUri(this IMetaValue metaValue)
        {
            if (metaValue == null)
            {
                throw new ArgumentNullException(nameof(metaValue));
            }

            var options = new JsonWriterOptions
            {
                Indented = true
            };

            using (var stream = new MemoryStream())
            {
                using (var writer = new Utf8JsonWriter(stream, options))
                {
                    metaValue.Serialize(writer);
                }

                return(Encoding.UTF8.GetString(stream.ToArray()));
            }
        }
Пример #15
0
        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 == JsonTypeInfo.ObjectType && value != null)
                    {
                        inputType = value !.GetType();
                    }

                    WriteStack state = new WriteStack {
                        CancellationToken = cancellationToken
                    };
                    JsonConverter converterBase = state.Initialize(inputType, options, supportContinuation: true);

                    bool isFinalBlock;

                    try
                    {
                        do
                        {
                            state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold);

                            try
                            {
                                isFinalBlock = WriteCore(converterBase, writer, value, options, ref state);
                            }
                            finally
                            {
                                if (state.PendingAsyncDisposables?.Count > 0)
                                {
                                    await state.DisposePendingAsyncDisposables().ConfigureAwait(false);
                                }
                            }

                            await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false);

                            bufferWriter.Clear();

                            if (state.PendingTask is not null)
                            {
                                try
                                {
                                    await state.PendingTask.ConfigureAwait(false);
                                }
                                catch
                                {
                                    // Exceptions will be propagated elsewhere
                                    // TODO https://github.com/dotnet/runtime/issues/22144
                                }
                            }
                        } while (!isFinalBlock);
                    }
                    catch
                    {
                        await state.DisposePendingDisposablesOnExceptionAsync().ConfigureAwait(false);

                        throw;
                    }
                }
        }
Пример #16
0
 public Utf8JsonWriter(IBufferWriter <byte> writer, JsonWriterOptions options)
 {
 }
Пример #17
0
 /// <summary>
 /// Gets the json representation as Stream.
 /// </summary>
 /// <param name="json">The j.</param>
 /// <param name="options">The options.</param>
 /// <returns></returns>
 public static Stream ToStream(
     this JsonDocument json,
     JsonWriterOptions options = default)
 {
     return(json.RootElement.ToStream(options));
 }
Пример #18
0
 /// <summary>
 /// Gets the json representation as string.
 /// </summary>
 /// <param name="json">The j.</param>
 /// <param name="options">The options.</param>
 /// <returns></returns>
 public static string AsString(
     this JsonDocument json,
     JsonWriterOptions options = default)
 {
     return(json.RootElement.AsString(options));
 }
Пример #19
0
        private static async Task WriteStreamAsync <TValue>(
            Stream utf8Json,
            TValue value,
            JsonTypeInfo jsonTypeInfo,
            CancellationToken cancellationToken)
        {
            JsonSerializerOptions options       = jsonTypeInfo.Options;
            JsonWriterOptions     writerOptions = options.GetWriterOptions();

            using (var bufferWriter = new PooledByteBufferWriter(options.DefaultBufferSize))
                using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions))
                {
                    WriteStack state = new WriteStack {
                        CancellationToken = cancellationToken
                    };
                    JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true);

                    bool isFinalBlock;

                    try
                    {
                        do
                        {
                            state.FlushThreshold = (int)(bufferWriter.Capacity * FlushThreshold);

                            try
                            {
                                isFinalBlock = WriteCore(converter, writer, value, options, ref state);
                                await bufferWriter.WriteToStreamAsync(utf8Json, cancellationToken).ConfigureAwait(false);

                                bufferWriter.Clear();
                            }
                            finally
                            {
                                // Await any pending resumable converter tasks (currently these can only be IAsyncEnumerator.MoveNextAsync() tasks).
                                // Note that pending tasks are always awaited, even if an exception has been thrown or the cancellation token has fired.
                                if (state.PendingTask is not null)
                                {
                                    try
                                    {
                                        await state.PendingTask.ConfigureAwait(false);
                                    }
                                    catch
                                    {
                                        // Exceptions should only be propagated by the resuming converter
                                        // TODO https://github.com/dotnet/runtime/issues/22144
                                    }
                                }

                                // Dispose any pending async disposables (currently these can only be completed IAsyncEnumerators).
                                if (state.CompletedAsyncDisposables?.Count > 0)
                                {
                                    await state.DisposeCompletedAsyncDisposables().ConfigureAwait(false);
                                }
                            }
                        } while (!isFinalBlock);
                    }
                    catch
                    {
                        // On exception, walk the WriteStack for any orphaned disposables and try to dispose them.
                        await state.DisposePendingDisposablesOnExceptionAsync().ConfigureAwait(false);

                        throw;
                    }
                }
        }
Пример #20
0
 /// <summary>
 /// Constructs a new <see cref="Utf8JsonWriter"/> instance with a specified <paramref name="bufferWriter"/>.
 /// </summary>
 /// <param name="bufferWriter">An instance of <see cref="IBufferWriter{Byte}" /> used as a destination for writing JSON text into.</param>
 /// <param name="options">Defines the customized behavior of the <see cref="Utf8JsonWriter"/>
 /// By default, the <see cref="Utf8JsonWriter"/> writes JSON minimized (that is, with no extra whitespace)
 /// and validates that the JSON being written is structurally valid according to JSON RFC.</param>
 /// <exception cref="ArgumentNullException">
 /// Thrown when the instance of <see cref="IBufferWriter{Byte}" /> that is passed in is null.
 /// </exception>
 public Utf8JsonWriter(IBufferWriter <byte> bufferWriter, JsonWriterOptions options = default)
 {
     _output  = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter));
     _options = options;
 }