1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Text.Json.Serialization;
10 namespace System.Text.Json
12 public static partial class JsonSerializer
14 private static bool HandleDictionary(
15 JsonClassInfo elementClassInfo,
16 JsonSerializerOptions options,
17 Utf8JsonWriter writer,
20 JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
21 if (state.Current.CollectionEnumerator == null)
23 IEnumerable enumerable;
25 enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue);
26 if (enumerable == null)
28 if ((state.Current.JsonClassInfo.ClassType != ClassType.Object || // Write null dictionary values
29 !state.Current.JsonPropertyInfo.IgnoreNullValues) && // Ignore ClassType.Object properties if IgnoreNullValues is true
30 state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) // Ignore null extension property (which is a dictionary)
32 // Write a null object or enumerable.
33 state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, options, writeNull: true);
36 if (state.Current.PopStackOnEndCollection)
44 // Let the dictionary return the default IEnumerator from its IEnumerable.GetEnumerator().
45 // For IDictionary-derived classes this is normally be IDictionaryEnumerator.
46 // For IDictionary<TKey, TVale>-derived classes this is normally IDictionaryEnumerator as well
47 // but may be IEnumerable<KeyValuePair<TKey, TValue>> if the dictionary only supports generics.
48 state.Current.CollectionEnumerator = enumerable.GetEnumerator();
50 if (state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing)
52 state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, options);
56 if (state.Current.CollectionEnumerator.MoveNext())
58 // A dictionary should not have a null KeyValuePair.
59 Debug.Assert(state.Current.CollectionEnumerator.Current != null);
61 bool obtainedValues = false;
63 object value = default;
65 // Check for polymorphism.
66 if (elementClassInfo.ClassType == ClassType.Unknown)
68 jsonPropertyInfo.GetDictionaryKeyAndValue(ref state.Current, out key, out value);
69 GetRuntimeClassInfo(value, ref elementClassInfo, options);
70 obtainedValues = true;
73 if (elementClassInfo.ClassType == ClassType.Value)
75 elementClassInfo.PolicyProperty.WriteDictionary(ref state, writer);
81 jsonPropertyInfo.GetDictionaryKeyAndValue(ref state.Current, out key, out value);
84 // An object or another enumerator requires a new stack frame.
85 state.Push(elementClassInfo, value);
86 state.Current.KeyName = key;
92 // We are done enumerating.
93 if (state.Current.ExtensionDataStatus == ExtensionDataWriteStatus.Writing)
95 state.Current.ExtensionDataStatus = ExtensionDataWriteStatus.Finished;
99 writer.WriteEndObject();
102 if (state.Current.PopStackOnEndCollection)
108 state.Current.EndDictionary();
114 internal static void WriteDictionary<TProperty>(
115 JsonConverter<TProperty> converter,
116 JsonSerializerOptions options,
117 ref WriteStackFrame current,
118 Utf8JsonWriter writer)
120 Debug.Assert(converter != null && current.CollectionEnumerator != null);
124 if (current.CollectionEnumerator is IEnumerator<KeyValuePair<string, TProperty>> enumerator)
126 key = enumerator.Current.Key;
127 value = enumerator.Current.Value;
129 else if (current.CollectionEnumerator is IEnumerator<KeyValuePair<string, object>> polymorphicEnumerator)
131 key = polymorphicEnumerator.Current.Key;
132 value = (TProperty)polymorphicEnumerator.Current.Value;
134 else if (current.CollectionEnumerator is IDictionaryEnumerator iDictionaryEnumerator &&
135 iDictionaryEnumerator.Key is string keyAsString)
138 value = (TProperty)iDictionaryEnumerator.Value;
142 throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
143 current.JsonPropertyInfo.DeclaredPropertyType,
144 current.JsonPropertyInfo.ParentClassType,
145 current.JsonPropertyInfo.PropertyInfo);
150 writer.WriteNull(key);
154 if (options.DictionaryKeyPolicy != null &&
155 current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) // We do not convert extension data.
157 key = options.DictionaryKeyPolicy.ConvertName(key);
161 ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(options.DictionaryKeyPolicy.GetType());
165 writer.WritePropertyName(key);
166 converter.Write(writer, value, options);