Beispiel #1
0
        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.
        }
Beispiel #2
0
        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();
        }
Beispiel #4
0
        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();
        }
Beispiel #5
0
        // 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);
        }
Beispiel #7
0
        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);
        }
Beispiel #10
0
        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);
        }
Beispiel #11
0
        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;
            }
        }
Beispiel #13
0
 protected internal abstract bool OnWriteResume(Utf8JsonWriter writer, TDictionary dictionary, JsonSerializerOptions options, ref WriteStack state);
Beispiel #14
0
        // 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));
        }
Beispiel #15
0
 // 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);
 }
Beispiel #16
0
 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);
 }
Beispiel #17
0
 /// <summary>
 /// Loosely-typed WriteCore() that forwards to strongly-typed WriteCore().
 /// </summary>
 internal abstract bool WriteCoreAsObject(Utf8JsonWriter writer, object?value, JsonSerializerOptions options, ref WriteStack state);
Beispiel #18
0
 // 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);
 }
Beispiel #19
0
 /// <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);
        }
Beispiel #21
0
        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);
        }
Beispiel #24
0
        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);
        }
Beispiel #25
0
        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);
        }
Beispiel #29
0
 /// <summary>
 /// Loosely-typed WriteWithQuotes() that forwards to strongly-typed WriteWithQuotes().
 /// </summary>
 internal abstract void WriteWithQuotesAsObject(Utf8JsonWriter writer, object value, JsonSerializerOptions options, ref WriteStack state);
Beispiel #30
0
        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);
        }