internal static void WriteDictionary <TProperty>( JsonConverter <TProperty> converter, JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer) { if (converter == null) { return; } Debug.Assert(current.CollectionEnumerator != null); string key; TProperty value; if (current.CollectionEnumerator is IEnumerator <KeyValuePair <string, TProperty> > enumerator) { // Avoid boxing for strongly-typed enumerators such as returned from IDictionary<string, TRuntimeProperty> value = enumerator.Current.Value; key = enumerator.Current.Key; } else if (current.CollectionEnumerator is IEnumerator <KeyValuePair <string, object> > polymorphicEnumerator) { value = (TProperty)polymorphicEnumerator.Current.Value; key = polymorphicEnumerator.Current.Key; } else if (current.IsIDictionaryConstructible || current.IsIDictionaryConstructibleProperty) { value = (TProperty)((DictionaryEntry)current.CollectionEnumerator.Current).Value; key = (string)((DictionaryEntry)current.CollectionEnumerator.Current).Key; } else { // Todo: support non-generic Dictionary here (IDictionaryEnumerator) throw new NotSupportedException(); } if (value == null) { writer.WriteNull(key); } else { if (options.DictionaryKeyPolicy != null && current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) // We do not convert extension data. { key = options.DictionaryKeyPolicy.ConvertName(key); if (key == null) { ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(options.DictionaryKeyPolicy.GetType()); } } JsonEncodedText escapedKey = JsonEncodedText.Encode(key, options.Encoder); writer.WritePropertyName(escapedKey); converter.Write(writer, value, options); } }
internal static void WriteDictionary <TProperty>( JsonConverter <TProperty> converter, JsonSerializerOptions options, ref WriteStackFrame current, Utf8JsonWriter writer) { Debug.Assert(converter != null && current.CollectionEnumerator != null); string key; TProperty value; if (current.CollectionEnumerator is IEnumerator <KeyValuePair <string, TProperty> > enumerator) { key = enumerator.Current.Key; value = enumerator.Current.Value; } else if (current.CollectionEnumerator is IEnumerator <KeyValuePair <string, object> > polymorphicEnumerator) { key = polymorphicEnumerator.Current.Key; value = (TProperty)polymorphicEnumerator.Current.Value; } else { if (((DictionaryEntry)current.CollectionEnumerator.Current).Key is string keyAsString) { key = keyAsString; value = (TProperty)((DictionaryEntry)current.CollectionEnumerator.Current).Value; } else { throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection( current.JsonPropertyInfo.DeclaredPropertyType, current.JsonPropertyInfo.ParentClassType, current.JsonPropertyInfo.PropertyInfo); } } if (value == null) { writer.WriteNull(key); } else { if (options.DictionaryKeyPolicy != null && current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) // We do not convert extension data. { key = options.DictionaryKeyPolicy.ConvertName(key); if (key == null) { ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(options.DictionaryKeyPolicy.GetType()); } } writer.WritePropertyName(key); converter.Write(writer, value, options); } }
protected override void OnWriteDictionary(ref WriteStackFrame current, Utf8JsonWriter writer) { Debug.Assert(Converter != null && current.CollectionEnumerator != null); string key = null; TProperty?value = null; if (current.CollectionEnumerator is IEnumerator <KeyValuePair <string, TProperty?> > enumerator) { key = enumerator.Current.Key; value = enumerator.Current.Value; } else { if (((DictionaryEntry)current.CollectionEnumerator.Current).Key is string keyAsString) { key = keyAsString; value = (TProperty?)((DictionaryEntry)current.CollectionEnumerator.Current).Value; } else { throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection( current.JsonPropertyInfo.DeclaredPropertyType, current.JsonPropertyInfo.ParentClassType, current.JsonPropertyInfo.PropertyInfo); } } Debug.Assert(key != null); if (Options.DictionaryKeyPolicy != null) { // We should not be in the Nullable-value implementation branch for extension data. // (TValue should be typeof(object) or typeof(JsonElement)). Debug.Assert(current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing); key = Options.DictionaryKeyPolicy.ConvertName(key); if (key == null) { ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(Options.DictionaryKeyPolicy.GetType()); } } if (value == null) { writer.WriteNull(key); } else { writer.WritePropertyName(key); Converter.Write(writer, value.GetValueOrDefault(), Options); } }
protected override void OnWriteDictionary(ref WriteStackFrame current, Utf8JsonWriter writer) { Debug.Assert(Converter != null && current.CollectionEnumerator != null); string key = null; TProperty?value = null; if (current.CollectionEnumerator is IEnumerator <KeyValuePair <string, TProperty?> > enumerator) { key = enumerator.Current.Key; value = enumerator.Current.Value; } else { if (((DictionaryEntry)current.CollectionEnumerator.Current).Key is string keyAsString) { key = keyAsString; value = (TProperty?)((DictionaryEntry)current.CollectionEnumerator.Current).Value; } else { throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection( current.JsonPropertyInfo.DeclaredPropertyType, current.JsonPropertyInfo.ParentClassType, current.JsonPropertyInfo.PropertyInfo); } } Debug.Assert(key != null); if (value == null) { writer.WriteNull(key); } else { if (Options.DictionaryKeyPolicy != null) { key = Options.DictionaryKeyPolicy.ConvertName(key); if (key == null) { ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(Options.DictionaryKeyPolicy.GetType()); } } writer.WritePropertyName(key); Converter.Write(writer, value.GetValueOrDefault(), Options); } }
protected override void OnWriteDictionary(ref WriteStackFrame current, Utf8JsonWriter writer) { Debug.Assert(Converter != null && current.CollectionEnumerator != null); string key = null; TProperty?value = null; if (current.CollectionEnumerator is IEnumerator <KeyValuePair <string, TProperty?> > enumerator) { key = enumerator.Current.Key; value = enumerator.Current.Value; } else if (current.IsIDictionaryConstructible || current.IsIDictionaryConstructibleProperty) { key = (string)((DictionaryEntry)current.CollectionEnumerator.Current).Key; value = (TProperty?)((DictionaryEntry)current.CollectionEnumerator.Current).Value; } Debug.Assert(key != null); if (value == null) { writer.WriteNull(key); } else { if (Options.DictionaryKeyPolicy != null) { key = Options.DictionaryKeyPolicy.ConvertName(key); if (key == null) { ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(Options.DictionaryKeyPolicy.GetType()); } } writer.WritePropertyName(key); Converter.Write(writer, value.GetValueOrDefault(), Options); } }
private static bool HandleDictionary( JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, ref WriteStack state) { JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo !; if (state.Current.CollectionEnumerator == null) { IEnumerable?enumerable = (IEnumerable?)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue); if (enumerable == null) { if ((state.Current.JsonClassInfo !.ClassType != ClassType.Object || // Write null dictionary values !jsonPropertyInfo.IgnoreNullValues) && // Ignore ClassType.Object properties if IgnoreNullValues is true state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) // Ignore null extension property (which is a dictionary) { // Write a null object or enumerable. state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, options, writeNull: true); } if (state.Current.PopStackOnEndCollection) { state.Pop(); } return(true); } if (state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) { if (options.ReferenceHandling.ShouldWritePreservedReferences()) { if (WriteReference(ref state, writer, options, ClassType.Dictionary, enumerable)) { return(WriteEndDictionary(ref state)); } } else { state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, options); } } // Let the dictionary return the default IEnumerator from its IEnumerable.GetEnumerator(). // For IDictionary-derived classes this is normally be IDictionaryEnumerator. // For IDictionary<TKey, TVale>-derived classes this is normally IDictionaryEnumerator as well // but may be IEnumerable<KeyValuePair<TKey, TValue>> if the dictionary only supports generics. state.Current.CollectionEnumerator = enumerable.GetEnumerator(); } if (state.Current.CollectionEnumerator.MoveNext()) { // A dictionary should not have a null KeyValuePair. Debug.Assert(state.Current.CollectionEnumerator.Current != null); bool obtainedValues = false; string?key = default; object?value = default; // Check for polymorphism. if (elementClassInfo.ClassType == ClassType.Unknown) { jsonPropertyInfo.GetDictionaryKeyAndValue(ref state.Current, out key, out value); GetRuntimeClassInfo(value, ref elementClassInfo, options); obtainedValues = true; } if (elementClassInfo.ClassType == ClassType.Value) { elementClassInfo.PolicyProperty !.WriteDictionary(ref state, writer); } else { if (!obtainedValues) { jsonPropertyInfo.GetDictionaryKeyAndValue(ref state.Current, out key, out value); } if (options.DictionaryKeyPolicy != null && state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) { Debug.Assert(key != null); key = options.DictionaryKeyPolicy.ConvertName(key); if (key == null) { ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(options.DictionaryKeyPolicy.GetType()); } } // An object or another enumerator requires a new stack frame. state.Push(elementClassInfo, value); state.Current.KeyName = key; } return(false); } // We are done enumerating. if (state.Current.ExtensionDataStatus == ExtensionDataWriteStatus.Writing) { state.Current.ExtensionDataStatus = ExtensionDataWriteStatus.Finished; } else { writer.WriteEndObject(); } return(WriteEndDictionary(ref state)); }
private static void HandlePropertyName( JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state) { if (state.Current.Drain) { return; } Debug.Assert(state.Current.ReturnValue != default || state.Current.TempDictionaryValues != default); Debug.Assert(state.Current.JsonClassInfo != default); if ((state.Current.IsProcessingDictionary || state.Current.IsProcessingIDictionaryConstructible) && state.Current.JsonClassInfo.DataExtensionProperty != state.Current.JsonPropertyInfo) { string keyName = reader.GetString(); if (options.DictionaryKeyPolicy != null) { keyName = options.DictionaryKeyPolicy.ConvertName(keyName); if (keyName == null) { ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(options.DictionaryKeyPolicy.GetType()); } keyName = options.DictionaryKeyPolicy.ConvertName(keyName); } if (state.Current.IsDictionary || state.Current.IsIDictionaryConstructible) { state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.PolicyProperty; } Debug.Assert( state.Current.IsDictionary || (state.Current.IsDictionaryProperty && state.Current.JsonPropertyInfo != null) || state.Current.IsIDictionaryConstructible || (state.Current.IsIDictionaryConstructibleProperty && state.Current.JsonPropertyInfo != null)); state.Current.KeyName = keyName; } else { state.Current.EndProperty(); ReadOnlySpan <byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; if (reader._stringHasEscaping) { int idx = propertyName.IndexOf(JsonConstants.BackSlash); Debug.Assert(idx != -1); propertyName = GetUnescapedString(propertyName, idx); } JsonPropertyInfo jsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(propertyName, ref state.Current); if (jsonPropertyInfo == null) { JsonPropertyInfo dataExtProperty = state.Current.JsonClassInfo.DataExtensionProperty; if (dataExtProperty == null) { state.Current.JsonPropertyInfo = JsonPropertyInfo.s_missingProperty; } else { state.Current.JsonPropertyInfo = dataExtProperty; state.Current.JsonPropertyName = propertyName.ToArray(); state.Current.KeyName = JsonHelpers.Utf8GetString(propertyName); state.Current.CollectionPropertyInitialized = true; CreateDataExtensionProperty(dataExtProperty, ref state); } } else { // Support JsonException.Path. Debug.Assert( jsonPropertyInfo.JsonPropertyName == null || options.PropertyNameCaseInsensitive || propertyName.SequenceEqual(jsonPropertyInfo.JsonPropertyName)); state.Current.JsonPropertyInfo = jsonPropertyInfo; if (jsonPropertyInfo.JsonPropertyName == null) { byte[] propertyNameArray = propertyName.ToArray(); if (options.PropertyNameCaseInsensitive) { // Each payload can have a different name here; remember the value on the temporary stack. state.Current.JsonPropertyName = propertyNameArray; } else { // Prevent future allocs by caching globally on the JsonPropertyInfo which is specific to a Type+PropertyName // so it will match the incoming payload except when case insensitivity is enabled (which is handled above). state.Current.JsonPropertyInfo.JsonPropertyName = propertyNameArray; } } state.Current.PropertyIndex++; } } }