Skip to content

Commit 8e47775

Browse files
authored
Fix MemoryStream Converter when serializing (#6888)
1 parent 0af2f93 commit 8e47775

File tree

1 file changed

+107
-37
lines changed

1 file changed

+107
-37
lines changed

libraries/Microsoft.Bot.Schema/Converters/AttachmentMemoryStreamConverter.cs

Lines changed: 107 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
6161
reader.Read();
6262
}
6363

64-
if (HaveStreams(list))
64+
if (HasMemoryStream(list))
6565
{
6666
return list;
6767
}
@@ -103,8 +103,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
103103
dict.Add(key, item);
104104
}
105105

106-
var list = dict.Values.ToList();
107-
if (HaveStreams(list))
106+
if (HasMemoryStream(dict))
108107
{
109108
return dict;
110109
}
@@ -119,68 +118,139 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
119118

120119
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
121120
{
122-
if (!typeof(MemoryStream).IsAssignableFrom(value.GetType()))
121+
if (HasMemoryStream(value))
123122
{
124-
if (value.GetType().GetInterface(nameof(IEnumerable)) != null)
123+
InternalWriteJson(writer, value, serializer);
124+
return;
125+
}
126+
127+
JToken.FromObject(value, serializer).WriteTo(writer);
128+
}
129+
130+
private static void InternalWriteJson(JsonWriter writer, object value, JsonSerializer serializer)
131+
{
132+
if (value == null || value is string)
133+
{
134+
// Avoid processing strings, since they implement IEnumerable.
135+
JToken.FromObject(value, serializer).WriteTo(writer);
136+
return;
137+
}
138+
139+
if (value is MemoryStream)
140+
{
141+
var buffer = (value as MemoryStream).ToArray();
142+
var result = new SerializedMemoryStream
143+
{
144+
Type = nameof(MemoryStream),
145+
Buffer = buffer.ToList()
146+
};
147+
148+
JToken.FromObject(result, serializer).WriteTo(writer);
149+
return;
150+
}
151+
152+
if (value is IDictionary dictionary)
153+
{
154+
writer.WriteStartObject();
155+
foreach (DictionaryEntry entry in dictionary)
125156
{
126-
// This makes the WriteJson loops over nested values to replace all instances of MemoryStream.
127-
serializer.Converters.Add(this);
157+
writer.WritePropertyName(entry.Key.ToString());
158+
InternalWriteJson(writer, entry.Value, serializer);
128159
}
129160

130-
JToken.FromObject(value, serializer).WriteTo(writer);
131-
serializer.Converters.Remove(this);
161+
writer.WriteEndObject();
162+
return;
163+
}
164+
165+
if (value is IEnumerable collection)
166+
{
167+
writer.WriteStartArray();
168+
foreach (var item in collection)
169+
{
170+
InternalWriteJson(writer, item, serializer);
171+
}
172+
173+
writer.WriteEndArray();
132174
return;
133175
}
134176

135-
var buffer = (value as MemoryStream).ToArray();
136-
var result = new SerializedMemoryStream
177+
var type = value.GetType();
178+
if (type.IsClass)
137179
{
138-
Type = nameof(MemoryStream),
139-
Buffer = buffer.ToList()
140-
};
180+
writer.WriteStartObject();
181+
foreach (var prop in type.GetProperties())
182+
{
183+
writer.WritePropertyName(prop.Name);
184+
InternalWriteJson(writer, prop.GetValue(value), serializer);
185+
}
141186

142-
JToken.FromObject(result).WriteTo(writer);
187+
writer.WriteEndObject();
188+
return;
189+
}
190+
191+
JToken.FromObject(value, serializer).WriteTo(writer);
143192
}
144193

145194
/// <summary>
146-
/// Check if a List contains at least one MemoryStream.
195+
/// Check if an object contains at least one MemoryStream.
147196
/// </summary>
148-
/// <param name="list">List of values that might have a MemoryStream instance.</param>
197+
/// <param name="value">Object contaning values that might have a MemoryStream instance.</param>
149198
/// <returns>True if there is at least one MemoryStream in the list, otherwise false.</returns>
150-
private static bool HaveStreams(List<object> list)
199+
private static bool HasMemoryStream(object value)
151200
{
152-
var result = false;
153-
foreach (var nextLevel in list)
201+
if (value == null || value is string)
154202
{
155-
if (nextLevel == null)
156-
{
157-
continue;
158-
}
203+
// Avoid processing strings, since they implement IEnumerable.
204+
return false;
205+
}
159206

160-
if (nextLevel.GetType() == typeof(MemoryStream))
161-
{
162-
result = true;
163-
}
207+
if (value is MemoryStream)
208+
{
209+
return true;
210+
}
164211

165-
// Type generated from the ReadJson => JsonToken.StartObject.
166-
if (nextLevel.GetType() == typeof(Dictionary<string, object>))
212+
if (value is IDictionary dictionary)
213+
{
214+
foreach (DictionaryEntry entry in dictionary)
167215
{
168-
result = HaveStreams((nextLevel as Dictionary<string, object>).Values.ToList());
216+
if (HasMemoryStream(entry.Value))
217+
{
218+
return true;
219+
}
169220
}
170221

171-
// Type generated from the ReadJson => JsonToken.StartArray.
172-
if (nextLevel.GetType() == typeof(List<object>))
222+
return false;
223+
}
224+
225+
if (value is IEnumerable collection)
226+
{
227+
foreach (var item in collection)
173228
{
174-
result = HaveStreams(nextLevel as List<object>);
229+
if (HasMemoryStream(item))
230+
{
231+
return true;
232+
}
175233
}
176234

177-
if (result)
235+
return false;
236+
}
237+
238+
var type = value.GetType();
239+
if (type.IsClass)
240+
{
241+
foreach (var prop in type.GetProperties())
178242
{
179-
break;
243+
var propValue = prop.GetValue(value);
244+
if (HasMemoryStream(propValue))
245+
{
246+
return true;
247+
}
180248
}
249+
250+
return false;
181251
}
182252

183-
return result;
253+
return false;
184254
}
185255

186256
internal class SerializedMemoryStream

0 commit comments

Comments
 (0)