Avro.Cadabra.Core 1.0.3.3

dotnet add package Avro.Cadabra.Core --version 1.0.3.3                
NuGet\Install-Package Avro.Cadabra.Core -Version 1.0.3.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Avro.Cadabra.Core" Version="1.0.3.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Avro.Cadabra.Core --version 1.0.3.3                
#r "nuget: Avro.Cadabra.Core, 1.0.3.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Avro.Cadabra.Core as a Cake Addin
#addin nuget:?package=Avro.Cadabra.Core&version=1.0.3.3

// Install Avro.Cadabra.Core as a Cake Tool
#tool nuget:?package=Avro.Cadabra.Core&version=1.0.3.3                

AvroCadabra

A tiny library to convert a user-defined C# object to an AvroRecord and back.

Useful when you need to use Avro to serialize your DTOs (data transfer objects) but can't afford to or not allowed to redesign just to make it work.

Another is for instances when you only want to serialize certain properties of your model, which can be driven by the schema provided.

Syntax:

// To convert an object instance to AvroRecord
myObject.ToAvroRecord(schema);

// To convert an AvroRecord to an object instance
avroRecord.FromAvroRecord<MyObjectClass>();

// or if you have a newer schema
avroRecord.FromAvroRecord<MyObjectClass>(newerSchema);

Example:

public class ShapeBasket
{
    public IList<IShape> Shapes { get; set; }
}

void main()
{
    var instance = new ShapeBasket
    {
        Shapes = new List<IShape>
        {
            new Circle { Name = "Red Dot", Radius = 15, Color = BasicColor.Red },
            new Square { Name = "Blue Square", Width = 20, Color = BasicColor.Blue }
        }
    };

    var avroFile = Path.GetTempFileName();

    // assuming you have a schema stored as a resource
    var schema_v1 = Encoding.Default.GetString(Resources.ShapeBasket_v1_0);

    // serialize to file
    using (var fs = new FileStream(avroFile, FileMode.Create))
    {
        using var writer = AvroContainer.CreateGenericWriter(schema_v1, fs, Codec.Deflate);
        using var sequentialWriter = new SequentialWriter<object>(writer, 1);

        // convert the instance to an AvroRecord
        sequentialWriter.Write(instance.ToAvroRecord(schema));
    }

    // assuming you want to deserialize using an updated schema
    var schema_v2 = Encoding.Default.GetString(Resources.ShapeBasket_v2_0);

    // deserialize to target type
    var target = new ShapeBasket();
    using (var fs = new FileStream(avroFile, FileMode.Open))
    {
        using var reader = AvroContainer.CreateGenericReader(fs);
        using var sequentialReader = new SequentialReader<object>(reader);

        // convert the AvroRecord to the actual instance
        target = sequentialReader.Objects.Cast<AvroRecord>()
            .Select(r => r.FromAvroRecord<ShapeBasket>()).FirstOrDefault();
    }
}

No Schema Serialization

Having an explicitly written schema offers more flexibility and gives you more control when serializing in Avro but if you have no special requirements and your types are simple and straightforward you can let ToAvroRecord infer the schema through reflection and serialize your objects.

Example (Ran in LINQPad):

void Main()
{
	var instance = new ShapeBasket
	{
		Shapes = new List<IShape>
				{
					new Circle(),
					new Circle {Name = "Red Dot", Radius = 15},
					new Square {Name = "Blue Square", Width = 20}
				}
	};

	var record = instance.ToAvroRecord();

	record.FromAvroRecord<ShapeBasket>().Dump();
	typeof(ShapeBasket).GetAvroSchema().ToString().Dump();
}

public class ShapeBasket
{
	public IList<IShape> Shapes { get; set; }
}

public interface IShape
{
	string Name { get; set; }
}

public class Square : IShape
{
	public string Name { get; set; }
	public double Width { get; set; }
}

public class Circle : IShape
{
	public string Name { get; set; }
	public double Radius { get; set; }
}

Result:

alternate text is missing from this package README image

Custom Field Processing

For instances where you need to extract values from your object beyond the usual way of exposing them via public property getters when serializing, and/or assigning them back to your object via ways other than through public property setters when deserializing e.g. calling a method or assigning them to private fields using .NET reflection.

Example:


using System;
using System.Text;
using Gooseman.Avro.Utility;
using Microsoft.Hadoop.Avro.Schema;

namespace TestAvro
{
    class Program
    {
        static void Main(string[] args)
        {
            var schema =
                @"{
                    ""type"": ""record"",
                    ""name"": ""TestAvro.SecretMessage"",
                    ""fields"": [
                        {
                            ""name"": ""_id"",
                            ""type"": ""string""
                        },
                        {
                            ""name"": ""Message"",
                            ""type"": ""string""
                        }
                    ]
                }";

            var secretMessage = new SecretMessage { Message = "Hello There!" };
            var avro = secretMessage.ToAvroRecord(schema, new SecretMessageValueGetter());
            var secretMessageReveal = avro.FromAvroRecord<SecretMessage>
                (customValueSetter: new SecretMessageValueSetter());

            Console.WriteLine(@$"Original Message: {secretMessage.Message} \r\n
                Sent Message: {avro[1]} \r\n
                Received Message: {secretMessageReveal.Message}");

            Console.ReadLine();
        }
    }

    public class SecretMessage
    {
        private Guid _id = Guid.NewGuid();
        public Guid Id => _id;
        public string Message { get; set; }
    }

    public class SecretMessageValueGetter : ICustomValueGetter
    {
        public object GetValue(object managedObject, string member)
        {
            switch (member)
            {
                case "_id":
                    return managedObject.GetFieldValue(member);
                case "Message":
                    var sm = ((dynamic) managedObject).Message;
                    return Convert.ToBase64String(Encoding.Default.GetBytes(sm));
                default:
                    return null;
            }
        }
    }

    public class SecretMessageValueSetter : ICustomValueSetter
    {
        public bool SetValue(object managedObject, string member, object value)
        {
            switch (member)
            {
                case "_id":
                    managedObject.SetFieldValue(member, value);
                    return true;
                case "Message":
                    managedObject.SetPropertyValue(member,
                        Encoding.Default.GetString(Convert.FromBase64String(value.ToString())));
                    return true;
            }

            return false;
        }
    }
}

Result:

Original Message: Hello There!
Sent Message: SGVsbG8gVGhlcmUh
Received Message: Hello There!

Available in Nuget

PM> Install-Package Avro.Cadabra.Core
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 is compatible.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net451 is compatible.  net452 was computed.  net46 was computed.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.3.3 11,152 3/25/2020
1.0.3.2 461 3/16/2020
1.0.3.1 505 3/11/2020
1.0.3 472 2/26/2020
1.0.2-alpha 366 2/19/2020
1.0.1.2 534 2/17/2020
1.0.1.1 573 2/16/2020