d221a5d197c6b4a254c1ce40a44bb08d165b14e5
[platform/upstream/dotnet/runtime.git] /
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.
4
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.Text.Json.Serialization;
9
10 namespace System.Text.Json
11 {
12     public static partial class JsonSerializer
13     {
14         private static bool HandleDictionary(
15             JsonClassInfo elementClassInfo,
16             JsonSerializerOptions options,
17             Utf8JsonWriter writer,
18             ref WriteStack state)
19         {
20             JsonPropertyInfo jsonPropertyInfo = state.Current.JsonPropertyInfo;
21             if (state.Current.CollectionEnumerator == null)
22             {
23                 IEnumerable enumerable;
24
25                 enumerable = (IEnumerable)jsonPropertyInfo.GetValueAsObject(state.Current.CurrentValue);
26                 if (enumerable == null)
27                 {
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)
31                     {
32                         // Write a null object or enumerable.
33                         state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, options, writeNull: true);
34                     }
35
36                     if (state.Current.PopStackOnEndCollection)
37                     {
38                         state.Pop();
39                     }
40
41                     return true;
42                 }
43
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();
49
50                 if (state.Current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing)
51                 {
52                     state.Current.WriteObjectOrArrayStart(ClassType.Dictionary, writer, options);
53                 }
54             }
55
56             if (state.Current.CollectionEnumerator.MoveNext())
57             {
58                 // A dictionary should not have a null KeyValuePair.
59                 Debug.Assert(state.Current.CollectionEnumerator.Current != null);
60
61                 bool obtainedValues = false;
62                 string key = default;
63                 object value = default;
64
65                 // Check for polymorphism.
66                 if (elementClassInfo.ClassType == ClassType.Unknown)
67                 {
68                     jsonPropertyInfo.GetDictionaryKeyAndValue(ref state.Current, out key, out value);
69                     GetRuntimeClassInfo(value, ref elementClassInfo, options);
70                     obtainedValues = true;
71                 }
72
73                 if (elementClassInfo.ClassType == ClassType.Value)
74                 {
75                     elementClassInfo.PolicyProperty.WriteDictionary(ref state, writer);
76                 }
77                 else
78                 {
79                     if (!obtainedValues)
80                     {
81                         jsonPropertyInfo.GetDictionaryKeyAndValue(ref state.Current, out key, out value);
82                     }
83
84                     // An object or another enumerator requires a new stack frame.
85                     state.Push(elementClassInfo, value);
86                     state.Current.KeyName = key;
87                 }
88
89                 return false;
90             }
91
92             // We are done enumerating.
93             if (state.Current.ExtensionDataStatus == ExtensionDataWriteStatus.Writing)
94             {
95                 state.Current.ExtensionDataStatus = ExtensionDataWriteStatus.Finished;
96             }
97             else
98             {
99                 writer.WriteEndObject();
100             }
101
102             if (state.Current.PopStackOnEndCollection)
103             {
104                 state.Pop();
105             }
106             else
107             {
108                 state.Current.EndDictionary();
109             }
110
111             return true;
112         }
113
114         internal static void WriteDictionary<TProperty>(
115             JsonConverter<TProperty> converter,
116             JsonSerializerOptions options,
117             ref WriteStackFrame current,
118             Utf8JsonWriter writer)
119         {
120             Debug.Assert(converter != null && current.CollectionEnumerator != null);
121
122             string key;
123             TProperty value;
124             if (current.CollectionEnumerator is IEnumerator<KeyValuePair<string, TProperty>> enumerator)
125             {
126                 key = enumerator.Current.Key;
127                 value = enumerator.Current.Value;
128             }
129             else if (current.CollectionEnumerator is IEnumerator<KeyValuePair<string, object>> polymorphicEnumerator)
130             {
131                 key = polymorphicEnumerator.Current.Key;
132                 value = (TProperty)polymorphicEnumerator.Current.Value;
133             }
134             else if (current.CollectionEnumerator is IDictionaryEnumerator iDictionaryEnumerator &&
135                 iDictionaryEnumerator.Key is string keyAsString)
136             {
137                 key = keyAsString;
138                 value = (TProperty)iDictionaryEnumerator.Value;
139             }
140             else
141             {
142                 throw ThrowHelper.GetNotSupportedException_SerializationNotSupportedCollection(
143                     current.JsonPropertyInfo.DeclaredPropertyType,
144                     current.JsonPropertyInfo.ParentClassType,
145                     current.JsonPropertyInfo.PropertyInfo);
146             }
147
148             if (value == null)
149             {
150                 writer.WriteNull(key);
151             }
152             else
153             {
154                 if (options.DictionaryKeyPolicy != null &&
155                     current.ExtensionDataStatus != ExtensionDataWriteStatus.Writing) // We do not convert extension data.
156                 {
157                     key = options.DictionaryKeyPolicy.ConvertName(key);
158
159                     if (key == null)
160                     {
161                         ThrowHelper.ThrowInvalidOperationException_SerializerDictionaryKeyNull(options.DictionaryKeyPolicy.GetType());
162                     }
163                 }
164
165                 writer.WritePropertyName(key);
166                 converter.Write(writer, value, options);
167             }
168         }
169     }
170 }