Десериализовать файл Avro с помощью C#
Я не могу найти способ десериализовать файл Apache Avro с помощью C#. Файл Avro-это файл, созданный архив в концентраторах событий Microsoft Azure.
С Java я могу использовать Инструменты Avro из Apache для преобразования файла в JSON:
java -jar avro-tools-1.8.1.jar tojson --pretty inputfile > output.json
использование пакета NuGet Microsoft.платформа Hadoop.Авро!--18--> я могу извлечь SequenceNumber
, Offset
и EnqueuedTimeUtc
, но так как я не знаю, какой тип использовать для Body
исключение заброшенный. Я пробовал с Dictionary<string, object>
и прочая.
static void Main(string[] args)
{
var fileName = "...";
using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var reader = AvroContainer.CreateReader<EventData>(stream))
{
using (var streamReader = new SequentialReader<EventData>(reader))
{
var record = streamReader.Objects.FirstOrDefault();
}
}
}
}
[DataContract(Namespace = "Microsoft.ServiceBus.Messaging")]
public class EventData
{
[DataMember(Name = "SequenceNumber")]
public long SequenceNumber { get; set; }
[DataMember(Name = "Offset")]
public string Offset { get; set; }
[DataMember(Name = "EnqueuedTimeUtc")]
public string EnqueuedTimeUtc { get; set; }
[DataMember(Name = "Body")]
public foo Body { get; set; }
// More properties...
}
схема выглядит так:
{
"type": "record",
"name": "EventData",
"namespace": "Microsoft.ServiceBus.Messaging",
"fields": [
{
"name": "SequenceNumber",
"type": "long"
},
{
"name": "Offset",
"type": "string"
},
{
"name": "EnqueuedTimeUtc",
"type": "string"
},
{
"name": "SystemProperties",
"type": {
"type": "map",
"values": [ "long", "double", "string", "bytes" ]
}
},
{
"name": "Properties",
"type": {
"type": "map",
"values": [ "long", "double", "string", "bytes" ]
}
},
{
"name": "Body",
"type": [ "null", "bytes" ]
}
]
}
4 ответов
я смог получить полный доступ к данным, используя dynamic
. Вот код для доступа к raw body
данные, которые хранятся в виде массива байтов. В моем случае эти байты содержат кодированный UTF8 JSON, но, конечно, это зависит от того, как вы изначально создали свой EventData
экземпляры, опубликованные в концентраторе событий:
using (var reader = AvroContainer.CreateGenericReader(stream))
{
while (reader.MoveNext())
{
foreach (dynamic record in reader.Current.Objects)
{
var sequenceNumber = record.SequenceNumber;
var bodyText = Encoding.UTF8.GetString(record.Body);
Console.WriteLine($"{sequenceNumber}: {bodyText}");
}
}
}
если кто-то может опубликовать статически типизированное решение, я его озвучу, но, учитывая, что большая задержка в любой системе почти наверняка будет подключение к архивным блокам концентратора событий, я бы не беспокоился о производительности синтаксического анализа. :)
этой суть показывает, как десериализовать захват концентратора событий с помощью C# с помощью Microsoft.платформа Hadoop.Avro2, который имеет то преимущество, что .NET Framework 4.5 и .NET Standard 1.6 совместимы:
var connectionString = "<Azure event hub capture storage account connection string>";
var containerName = "<Azure event hub capture container name>";
var blobName = "<Azure event hub capture BLOB name (ends in .avro)>";
var storageAccount = CloudStorageAccount.Parse(connectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(containerName);
var blob = container.GetBlockBlobReference(blobName);
using (var stream = blob.OpenRead())
using (var reader = AvroContainer.CreateGenericReader(stream))
while (reader.MoveNext())
foreach (dynamic result in reader.Current.Objects)
{
var record = new AvroEventData(result);
record.Dump();
}
public struct AvroEventData
{
public AvroEventData(dynamic record)
{
SequenceNumber = (long) record.SequenceNumber;
Offset = (string) record.Offset;
DateTime.TryParse((string) record.EnqueuedTimeUtc, out var enqueuedTimeUtc);
EnqueuedTimeUtc = enqueuedTimeUtc;
SystemProperties = (Dictionary<string, object>) record.SystemProperties;
Properties = (Dictionary<string, object>) record.Properties;
Body = (byte[]) record.Body;
}
public long SequenceNumber { get; set; }
public string Offset { get; set; }
public DateTime EnqueuedTimeUtc { get; set; }
public Dictionary<string, object> SystemProperties { get; set; }
public Dictionary<string, object> Properties { get; set; }
public byte[] Body { get; set; }
}
-
NuGet для ссылки:
- Microsoft.платформа Hadoop.Avro2 (1.2.1 работает)
- на сайте windowsazure.Хранения (8.3.0 работает)
-
пространства имен:
- Microsoft.платформа Hadoop.Авро.Контейнер
- Microsoft.На сайте windowsazure.Хранение
Я, наконец, смог заставить это работать с библиотекой / фреймворком Apache C#.
Я застрял на некоторое время, потому что функция захвата концентраторов событий Azure иногда выводит файл без содержимого сообщения.
Возможно, у меня также была проблема с тем, как сообщения были первоначально сериализованы в объект EventData.
Приведенный ниже код предназначен для файла, сохраненного на диск из контейнера blob capture.
var dataFileReader = DataFileReader<EventData>.OpenReader(file);
foreach (var record in dataFileReader.NextEntries)
{
// Do work on EventData object
}
Это также работает с использованием GenericRecord объект.
var dataFileReader = DataFileReader<GenericRecord>.OpenReader(file);
Это потребовало некоторых усилий, чтобы выяснить. Однако теперь я согласен, что эта функция захвата концентраторов событий Azure-отличная функция для резервного копирования всех событий. Я по-прежнему считаю, что они должны сделать формат необязательным, как это было с выводом аналитических заданий Stream, но, возможно, я привыкну к Avro.
ваши оставшиеся типы, я подозреваю, должны быть определены как:
[DataContract(Namespace = "Microsoft.ServiceBus.Messaging")]
[KnownType(typeof(Dictionary<string, object>))]
public class EventData
{
[DataMember]
public IDictionary<string, object> SystemProperties { get; set; }
[DataMember]
public IDictionary<string, object> Properties { get; set; }
[DataMember]
public byte[] Body { get; set; }
}
хотя Body
Союз null
и bytes
, это сопоставляется с nullable
byte[]
.
в C# массивы всегда являются ссылочными типами, поэтому может быть null
и контракт выполнен.