使用C#

时间:2016-10-04 07:44:34

标签: c# azure hadoop avro

我找不到用C#反序列化Apache Avro文件的方法。 Avro文件是由Microsoft Azure事件中心中的Archive feature生成的文件。

使用Java,我可以使用Apache的Avro Tools将文件转换为JSON:

java -jar avro-tools-1.8.1.jar tojson --pretty inputfile > output.json

使用NuGet包 Microsoft.Hadoop.Avro 我可以提取SequenceNumberOffsetEnqueuedTimeUtc,但因为我不知道用于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" ]
    }
  ]
}    

6 个答案:

答案 0 :(得分:6)

这个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参考:

    • Microsoft.Hadoop.Avro2(1.2.1有效)
    • WindowsAzure.Storage(8.3.0正常工作)
  • 命名空间:

    • Microsoft.Hadoop.Avro.Container
    • Microsoft.WindowsAzure.Storage

答案 1 :(得分:5)

我能够使用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}");
        }
    }
}

如果有人可以发布一个静态类型的解决方案,我会对它进行投票,但鉴于任何系统中较大的延迟几乎肯定会与事件中心存档blob的连接,我不会担心关于解析性能。 :)

答案 2 :(得分:4)

我终于能够使用Apache C#库/框架了 我被困了一段时间,因为Azure事件中心的Capture功能有时会输出一个没有任何消息内容的文件。 我可能还有一个问题,即如何将消息最初序列化为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事件中心捕获功能是一个很好的功能来备份所有事件。我仍然认为他们应该像使用Stream Analytic作业输出那样使格式可选,但也许我会习惯Avro。

答案 3 :(得分:0)

您怀疑其余类型应定义为:

[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; }
}

即使Bodynullbytes的联盟,也会映射到nullable byte[]

在C#中,数组始终是引用类型,因此可以null并完成合同。

答案 4 :(得分:0)

您还可以使用NullableSchema属性将主体标记为字节和零的并集。这将允许您使用强类型接口。

[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; }
}

答案 5 :(得分:0)

对于在序列化/反序列化方面遇到麻烦的人们,我在C#中创建了一个小型库,该库是Microsoft.Hadoop.Avro的接口:

https://github.com/AdrianStrugala/AvroConvert

https://www.nuget.org/packages/AvroConvert

用法很简单:

byte[] avroFileContent = File.ReadAllBytes(fileName);
Dictionary<string, object> result = AvroConvert.Deserialize(avroFileContent);

//or if u know the model of the data
MyModel result = AvroConvert.Deserialize<MyModel>(avroFileContent);