diff --git a/libraries/Microsoft.Bot.Schema/Converters/AttachmentMemoryStreamConverter.cs b/libraries/Microsoft.Bot.Schema/Converters/AttachmentMemoryStreamConverter.cs index 8667bd215f..79676a54b6 100644 --- a/libraries/Microsoft.Bot.Schema/Converters/AttachmentMemoryStreamConverter.cs +++ b/libraries/Microsoft.Bot.Schema/Converters/AttachmentMemoryStreamConverter.cs @@ -61,7 +61,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist reader.Read(); } - if (HaveStreams(list)) + if (HasMemoryStream(list)) { return list; } @@ -103,8 +103,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist dict.Add(key, item); } - var list = dict.Values.ToList(); - if (HaveStreams(list)) + if (HasMemoryStream(dict)) { return dict; } @@ -119,68 +118,139 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - if (!typeof(MemoryStream).IsAssignableFrom(value.GetType())) + if (HasMemoryStream(value)) { - if (value.GetType().GetInterface(nameof(IEnumerable)) != null) + InternalWriteJson(writer, value, serializer); + return; + } + + JToken.FromObject(value, serializer).WriteTo(writer); + } + + private static void InternalWriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null || value is string) + { + // Avoid processing strings, since they implement IEnumerable. + JToken.FromObject(value, serializer).WriteTo(writer); + return; + } + + if (value is MemoryStream) + { + var buffer = (value as MemoryStream).ToArray(); + var result = new SerializedMemoryStream + { + Type = nameof(MemoryStream), + Buffer = buffer.ToList() + }; + + JToken.FromObject(result, serializer).WriteTo(writer); + return; + } + + if (value is IDictionary dictionary) + { + writer.WriteStartObject(); + foreach (DictionaryEntry entry in dictionary) { - // This makes the WriteJson loops over nested values to replace all instances of MemoryStream. - serializer.Converters.Add(this); + writer.WritePropertyName(entry.Key.ToString()); + InternalWriteJson(writer, entry.Value, serializer); } - JToken.FromObject(value, serializer).WriteTo(writer); - serializer.Converters.Remove(this); + writer.WriteEndObject(); + return; + } + + if (value is IEnumerable collection) + { + writer.WriteStartArray(); + foreach (var item in collection) + { + InternalWriteJson(writer, item, serializer); + } + + writer.WriteEndArray(); return; } - var buffer = (value as MemoryStream).ToArray(); - var result = new SerializedMemoryStream + var type = value.GetType(); + if (type.IsClass) { - Type = nameof(MemoryStream), - Buffer = buffer.ToList() - }; + writer.WriteStartObject(); + foreach (var prop in type.GetProperties()) + { + writer.WritePropertyName(prop.Name); + InternalWriteJson(writer, prop.GetValue(value), serializer); + } - JToken.FromObject(result).WriteTo(writer); + writer.WriteEndObject(); + return; + } + + JToken.FromObject(value, serializer).WriteTo(writer); } /// - /// Check if a List contains at least one MemoryStream. + /// Check if an object contains at least one MemoryStream. /// - /// List of values that might have a MemoryStream instance. + /// Object contaning values that might have a MemoryStream instance. /// True if there is at least one MemoryStream in the list, otherwise false. - private static bool HaveStreams(List list) + private static bool HasMemoryStream(object value) { - var result = false; - foreach (var nextLevel in list) + if (value == null || value is string) { - if (nextLevel == null) - { - continue; - } + // Avoid processing strings, since they implement IEnumerable. + return false; + } - if (nextLevel.GetType() == typeof(MemoryStream)) - { - result = true; - } + if (value is MemoryStream) + { + return true; + } - // Type generated from the ReadJson => JsonToken.StartObject. - if (nextLevel.GetType() == typeof(Dictionary)) + if (value is IDictionary dictionary) + { + foreach (DictionaryEntry entry in dictionary) { - result = HaveStreams((nextLevel as Dictionary).Values.ToList()); + if (HasMemoryStream(entry.Value)) + { + return true; + } } - // Type generated from the ReadJson => JsonToken.StartArray. - if (nextLevel.GetType() == typeof(List)) + return false; + } + + if (value is IEnumerable collection) + { + foreach (var item in collection) { - result = HaveStreams(nextLevel as List); + if (HasMemoryStream(item)) + { + return true; + } } - if (result) + return false; + } + + var type = value.GetType(); + if (type.IsClass) + { + foreach (var prop in type.GetProperties()) { - break; + var propValue = prop.GetValue(value); + if (HasMemoryStream(propValue)) + { + return true; + } } + + return false; } - return result; + return false; } internal class SerializedMemoryStream