C#でApache Avroファイルを逆シリアル化する方法が見つかりません。 Avroファイルは、Microsoft Azure Event Hubsの アーカイブ機能 によって生成されたファイルです。
Java Apacheから Avro Tools を使用してファイルをJSONに変換できます:
Java -jar avro-tools-1.8.1.jar tojson --pretty inputfile > output.json
NuGetパッケージの使用Microsoft.Hadoop.AvroSequenceNumber
、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" ]
}
]
}
dynamic
を使用して、完全なデータアクセスを機能させることができました。これは、バイトの配列として格納されている生の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}");
}
}
}
誰かが静的に型付けされたソリューションを投稿できる場合、私はそれを賛成しますが、どのシステムでも大きな待機時間がEvent Hub Archive BLOBへの接続になることを考えると、パフォーマンスの解析について心配する必要はありません。 :)
この Gist は、Microsoft.Hadoop.Avro2を使用してC#でイベントハブキャプチャを逆シリアル化する方法を示しています。これには、.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の参照:
名前空間:
これでようやくこれをApache C#ライブラリ/フレームワークで動作させることができました。
Azure Event Hubsのキャプチャ機能がメッセージコンテンツのないファイルを出力することがあるので、しばらくの間行き詰まっていました。メッセージがEventDataオブジェクトに最初にシリアル化された方法にも問題があった可能性があります。
以下のコードは、キャプチャーBLOBコンテナーからディスクに保存されたファイル用です。
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 Event Hubs Capture機能がすべてのイベントをバックアップする優れた機能であることに今、同意します。 Stream Analyticジョブ出力で行ったように、形式をオプションにする必要があると私はまだ感じていますが、多分Avroに慣れるでしょう。
NullableSchema
属性を使用して、本文をバイトの結合およびnullとしてマークすることもできます。これにより、厳密に型指定されたインターフェイスを使用できるようになります。
[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")]
[NullableSchema]
public foo Body { get; set; }
}
残りのタイプは、次のように定義する必要があると思います。
[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
にすることができ、契約が満たされます。
https://github.com/AdrianStrugala/AvroConvert を使用することをお勧めします
そして単に:
byte[] avroFileContent = File.ReadAllBytes(fileName);
var result = AvroConvert.Deserialize<EventData>(avroFileContent);
ライブラリ自体は、Avro形式を使用して開発フローを改善することを目的としています。モデルのスキーマや属性も必要ありません。 (私はこのライブラリの寄稿者です)