diff --git a/src/AvroConvert/AvroConvert.DeserializeContainer.cs b/src/AvroConvert/AvroConvert.DeserializeContainer.cs
new file mode 100644
index 00000000..0d334d88
--- /dev/null
+++ b/src/AvroConvert/AvroConvert.DeserializeContainer.cs
@@ -0,0 +1,146 @@
+#region license
+/**Copyright (c) 2021 Adrian Strugala
+*
+* Licensed under the CC BY-NC-SA 3.0 License(the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://creativecommons.org/licenses/by-nc-sa/3.0/
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+* You are free to use or modify the code for personal usage.
+* For commercial usage purchase the product at
+*
+* https://xabe.net/product/avroconvert/
+*/
+#endregion
+
+using System;
+using System.Collections;
+using System.IO;
+using SolTechnology.Avro.AvroObjectServices.BuildSchema;
+using SolTechnology.Avro.Features.Deserialize;
+using SolTechnology.Avro.Infrastructure.Exceptions;
+using SolTechnology.Avro.Infrastructure.Extensions;
+
+namespace SolTechnology.Avro
+{
+ public static partial class AvroConvert
+ {
+ ///
+ /// Deserializes Avro data from a byte array into a specified .NET type.
+ ///
+ /// The type of object to deserialize into.
+ /// The byte array containing the Avro data to be deserialized.
+ /// The deserialized object of the specified type.
+ ///
+ /// This method takes a byte array containing Avro data and deserializes it into an object of the specified type (generic parameter T).
+ ///
+ public static T DeserializeContainer(byte[] avroBytes)
+ where T: IEnumerable
+ {
+ var type = GetEnumerableType();
+
+ using (var stream = new MemoryStream(avroBytes))
+ {
+ var decoder = new Decoder();
+ var deserialized = decoder.Decode(
+ stream,
+ Schema.Create(type)
+ );
+ return deserialized;
+ }
+ }
+
+ ///
+ /// Deserializes Avro data from a byte array into a specified .NET type using the provided Avro conversion options.
+ ///
+ /// The type of object to deserialize into.
+ /// The byte array containing the Avro data to be deserialized.
+ /// The Avro conversion options that control the deserialization process.
+ /// The deserialized object of the specified type.
+ ///
+ /// This method takes a byte array containing Avro data and deserializes it into an object of the specified type (generic parameter T).
+ /// The deserialization process is controlled by the provided Avro conversion options.
+ ///
+ public static T DeserializeContainer(byte[] avroBytes, AvroConvertOptions options)
+ where T: IEnumerable
+ {
+ var type = GetEnumerableType();
+
+ using (var stream = new MemoryStream(avroBytes))
+ {
+ var decoder = new Decoder(options);
+ var deserialized = decoder.Decode(
+ stream,
+ Schema.Create(type)
+ );
+ return deserialized;
+ }
+ }
+
+ ///
+ /// Deserializes Avro data from a byte array into an object of the specified target type using reflection.
+ ///
+ /// The byte array containing the Avro data to be deserialized.
+ /// The target type into which the Avro data should be deserialized.
+ /// The deserialized object of the specified target type.
+ ///
+ /// This method uses reflection to dynamically invoke the generic method
+ /// to deserialize Avro data from a byte array into an object of the specified target type.
+ ///
+ public static dynamic DeserializeContainer(byte[] avroBytes, Type targetType)
+ {
+ object result = typeof(AvroConvert)
+ .GetMethod(nameof(DeserializeContainer), new[] { typeof(byte[]) })
+ ?.MakeGenericMethod(targetType)
+ .Invoke(null, new object[] { avroBytes });
+
+ return result;
+ }
+
+
+ ///
+ /// Deserializes Avro data from a of bytes into an object of the specified type.
+ ///
+ /// The type of object to deserialize into.
+ /// The of bytes containing the Avro data to be deserialized.
+ /// The deserialized object of the specified type.
+ ///
+ /// This method performs Avro data deserialization from a of bytes into an object of the specified type.
+ /// It is suitable for scenarios where minimizing memory allocation is crucial.
+ ///
+ public static unsafe T DeserializeContainer(ReadOnlySpan avroBytes)
+ where T: IEnumerable
+ {
+ var type = GetEnumerableType();
+
+ fixed (byte* ptr = avroBytes)
+ {
+ using UnmanagedMemoryStream stream = new(ptr, avroBytes.Length);
+ var decoder = new Decoder();
+ var obj = decoder.Decode(
+ stream,
+ Schema.Create(type)
+ );
+
+ return obj;
+ }
+ }
+
+ private static Type GetEnumerableType()
+ {
+ var type = typeof(T);
+
+ if (!type.IsEnumerable())
+ throw new AvroException("[IEnumerable] required to deserialize container but found " + type);
+
+ return type.GetEnumeratedType();
+ }
+ }
+}
diff --git a/src/AvroConvert/AvroConvert.SerializeContainer.cs b/src/AvroConvert/AvroConvert.SerializeContainer.cs
new file mode 100644
index 00000000..2d779974
--- /dev/null
+++ b/src/AvroConvert/AvroConvert.SerializeContainer.cs
@@ -0,0 +1,87 @@
+#region license
+/**Copyright (c) 2020 Adrian Strugała
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* https://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+#endregion
+
+using System.Collections.Generic;
+using System.IO;
+using SolTechnology.Avro.AvroObjectServices.BuildSchema;
+using SolTechnology.Avro.Features.Serialize;
+
+namespace SolTechnology.Avro
+{
+ public static partial class AvroConvert
+ {
+ ///
+ /// Serializes the given into Avro container format (including header with metadata)
+ ///
+ public static byte[] SerializeContainer(IEnumerable items)
+ {
+ return SerializeContainer(items, CodecType.Null);
+ }
+
+ ///
+ /// Serializes the given into Avro container format (including header with metadata)
+ /// Choosing reduces output object size
+ ///
+ public static byte[] SerializeContainer(IEnumerable items, CodecType codecType)
+ where T : notnull
+ {
+ using (MemoryStream resultStream = new MemoryStream())
+ {
+ var schema = Schema.Create(typeof(T));
+
+ using (var writer = new Encoder(schema, resultStream, codecType))
+ {
+ foreach (var item in items)
+ {
+ writer.Append(item);
+ }
+ }
+
+ byte[] result = resultStream.ToArray();
+ return result;
+ }
+ }
+
+ ///
+ /// Serializes the given into Avro container format (including header with metadata) and returns the serialized data as a byte array.
+ ///
+ /// The items to be serialized into Avro container format.
+ /// The Avro conversion options that control the serialization process.
+ /// A byte array containing the serialized Avro data.
+ ///
+ /// This method takes an and serializes it into Avro container format based on the provided Avro conversion options.
+ /// The resulting serialized data is returned as a byte array.
+ ///
+ public static byte[] SerializeContainer(IEnumerable items, AvroConvertOptions options)
+ where T : notnull
+ {
+ using (MemoryStream resultStream = new MemoryStream())
+ {
+ var schema = Schema.Create(typeof(T), options);
+ using (var writer = new Encoder(schema, resultStream, options.Codec, options))
+ {
+ foreach (var item in items)
+ {
+ writer.Append(item);
+ }
+ }
+ byte[] result = resultStream.ToArray();
+ return result;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AvroConvert/AvroObjectServices/Read/Resolvers/Array.cs b/src/AvroConvert/AvroObjectServices/Read/Resolvers/Array.cs
index a3ca5ab5..35c8eaa7 100644
--- a/src/AvroConvert/AvroObjectServices/Read/Resolvers/Array.cs
+++ b/src/AvroConvert/AvroObjectServices/Read/Resolvers/Array.cs
@@ -41,7 +41,11 @@ internal object ResolveArray(TypeSchema writerSchema, TypeSchema readerSchema, I
{
writerSchema = ((ArraySchema)writerSchema).ItemSchema;
}
- readerSchema = ((ArraySchema)readerSchema).ItemSchema;
+
+ if (readerSchema is ArraySchema arraySchema)
+ {
+ readerSchema = arraySchema.ItemSchema;
+ }
if (type.IsDictionary())
{
diff --git a/src/AvroConvert/Infrastructure/Extensions/TypeExtensions.cs b/src/AvroConvert/Infrastructure/Extensions/TypeExtensions.cs
index f0c731c2..5ac42b75 100644
--- a/src/AvroConvert/Infrastructure/Extensions/TypeExtensions.cs
+++ b/src/AvroConvert/Infrastructure/Extensions/TypeExtensions.cs
@@ -417,6 +417,11 @@ internal static bool IsList(this Type type)
return typeof(IList).IsAssignableFrom(type);
}
+ internal static bool IsEnumerable(this Type type)
+ {
+ return typeof(IEnumerable).IsAssignableFrom(type);
+ }
+
internal static bool IsGuid(this Type type)
{
return type == typeof(Guid);
diff --git a/tests/AvroConvertTests/FullSerializationAndDeserialization/ContainerTests.cs b/tests/AvroConvertTests/FullSerializationAndDeserialization/ContainerTests.cs
new file mode 100644
index 00000000..10b61fb5
--- /dev/null
+++ b/tests/AvroConvertTests/FullSerializationAndDeserialization/ContainerTests.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using AutoFixture;
+using Xunit;
+
+namespace AvroConvertComponentTests.FullSerializationAndDeserialization
+{
+ public class ContainerTests
+ {
+ private readonly Fixture _fixture = new();
+
+ [Theory]
+ [MemberData(nameof(TestEngine.ContainerOnly), MemberType = typeof(TestEngine))]
+ public void AvroMap(Func