PageRenderTime 91ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/articles/hdinsight-dotnet-avro-serialization.md

https://github.com/deaquino/azure-content
Markdown | 1314 lines | 1074 code | 240 blank | 0 comment | 0 complexity | ac89c072fea1008e0d0aba443c326bc4 MD5 | raw file
  1. <properties linkid="hdinsight-dotnet-avro-serialization" urlDisplayName="HDInsight Microsoft .NET Library for Serialization with Avro" pageTitle="Serialize data with the Microsoft .NET Library for Avro | Azure" metaKeywords="" description="Learn how Azure HDInsight uses Avro to serialize big data." metaCanonical="" services="hdinsight" documentationCenter="" title="Serialize data with the Microsoft .NET Library for Avro " authors="bradsev" solutions="" manager="paulettm" editor="cgronlun" />
  2. <tags ms.service="hdinsight" ms.workload="big-data" ms.tgt_pltfrm="na" ms.devlang="na" ms.topic="article" ms.date="01/01/1900" ms.author="bradsev" />
  3. # Serialize data with the Microsoft .NET Library for Avro
  4. ##Overview
  5. This topic shows how to use the Microsoft .NET Library for Avro to serialize objects and other data structures into streams in order to persist them to memory, a database or a file, and also how to deserialize them to recover the original objects.
  6. ###Apache Avro
  7. The Microsoft .NET Library for Avro implements the Apache Avro data serialization system for the Microsoft.NET environment. Apache Avro provides a compact binary data interchange format for serialization. It uses [JSON](http://www.json.org) to define language agnostic schema that underwrites language interoperability. Data serialized in one language can be read in another. Currently C, C++, C#, Java, PHP, Python, and Ruby are supported. Detailed information on the format can be found in the [Apache Avro Specification](http://avro.apache.org/docs/current/spec.html). Note that the current version of the Microsoft .NET Library for Avro does not support the Remote Procedure Calls (RPC) part of this specification.
  8. The serialized representation of an object in Avro system consists of two parts: schema and actual value. The Avro schema describes the language independent data model of the serialized data with JSON. It is present side-by-side with a binary representation of data. Having the schema separate from the binary representation permits each object to be written with no per-value overheads, making serialization fast and the representation small.
  9. ###The Hadoop scenario
  10. Apache Avro serialization format is widely used in Azure HDInsight and other Apache Hadoop environments. Avro provides a convenient way to represent complex data structures within a Hadoop MapReduce job. The format of Avro files has been designed to support the distributed MapReduce programming model. The key feature that enables the distribution is that the files are splittable in the sense that one can seek any point in a file and start reading from a particular block.
  11. ###Serialization in the Microsoft .NET Library for Avro
  12. The .NET Library for Avro supports two ways of serializing objects:
  13. - **reflection**: The JSON schema for the types is automatically built from the data contract attributes of the .NET types to be serialized.
  14. - **generic record**:The A JSON schema is explicitly specified in a record represented by the [**AvroRecord**](http://msdn.microsoft.com/en-us/library/microsoft.hadoop.avro.avrorecord.aspx) class when no .NET types are present to describe the schema for the data to be serialized.
  15. When the data schema is known to both the writer and reader of the stream, the data can be sent without its schema. But when this is not the case, the schema must be shared using an Avro container file. Other parameters such as the codec used for data compression can be specified. These scenarios are outlined in more detail and illustrated in the code examples below.
  16. ###Microsoft .NET Library for Avro prerequisites
  17. - [Microsoft .NET Framework v4.0](http://www.microsoft.com/en-us/download/details.aspx?id=17851)
  18. - [Newtonsoft Json.NET](http://james.newtonking.com/json) (v5.0.5 or later)
  19. Note that the Newtonsoft.Json.dll dependency is downloaded automatically with with the installation of the Microsoft .NET Library for Avro, the procedure for which is provided in the following section.
  20. ###Microsoft .NET Library for Avro installation
  21. The Microsoft .NET Library for Avro is distributed as a NuGet Package that can be installed from Visual Studio using the following procedure:
  22. - Select the **Project** tab -> **Manage NuGet Packages...**
  23. - Search for "Microsoft.Hadoop.Avro" in the **Online Search** box.
  24. - Click the **Install** button next to **Microsoft .NET Library for Avro**.
  25. Note that the Newtonsoft.Json.dll (>= .5.0.5) dependency is also downloaded automatically with with the Microsoft .NET Library for Avro.
  26. ##Guide to the samples
  27. Five examples provided in this topic each illustrate different scenarios supported by the Microsoft .NET Library for Avro.
  28. The first two show how to serialize and deserialize data into memory stream buffers using reflection and generic records. The schema in these two cases is assumed to be shared between the readers and writers out-of-band so that the schema does not need to be serialized with the data in an Avro container file.
  29. The third and fourth examples show how to serialize and deserialize data into memory stream buffers using reflection and generic record with Avro object container files. When data is stored in an Avro container file, its schema is always stored with it because the schema must be shared for deserialization.
  30. The sample containing the first four examples can be downloaded from [Azure code samples](http://code.msdn.microsoft.com/windowsazure/Serialize-data-with-the-86055923) site.
  31. The fifth and final example shows how to how to use a custom compression codec for object container files. A sample containing the code for this example can be downloaded from the [Azure code samples](http://code.msdn.microsoft.com/windowsazure/Serialize-data-with-the-67159111) site.
  32. The Microsoft .NET Library for Avro is designed to work with any stream. In these examples, data is manipulated using memory streams rather than file streams or databases for simplicity and consistency. The approach taken in a production environment will depend on the exact scenario requirements, data source and volume, performance constraints, and other factors.
  33. * <a href="#Scenario1">**Serialization with reflection**</a>: The JSON schema for types to be serialized is automatically built from the data contract attributes.
  34. * <a href="#Scenario2">**Serialization with generic record**</a>: The JSON schema is explicitly specified in a record when no .NET type is available for reflection.
  35. * <a href="#Scenario3">**Serialization using object container files with reflection**</a>: The JSON schema is implicitly serialized with the data and shared using an Avro container file.
  36. * <a href="#Scenario4">**Serialization using object container files with generic record**</a>: The JSON schema is explicitly serialized with the data and shared using an Avro container file.
  37. * <a href="#Scenario5">**Serialization using object container files with a custom compression codec**</a>: The JSON schema is serialized with data and shared using an Avro container file with a customized .NET implementation of the deflate data compression codec.
  38. <h2> <a name="Scenario1"></a>Serialization with reflection</h2>
  39. The JSON schema for the types can be automatically built by Microsoft .NET Library for Avro using reflection from the data contract attributes of the C# objects to be serialized. Microsoft .NET Library for Avro creates an [**IAvroSeralizer<T>**](http://msdn.microsoft.com/en-us/library/dn627341.aspx) to identify the fields to be serialized.
  40. In this example objects (a **SensorData** class with a member **Location** struct) are serialized to a memory stream and this stream is in turn deserialized. The result is then compared to the initial instance to confirm that the **SensorData** object recovered is identical to original.
  41. The schema in this example is assumed to be shared between the readers and writers, so the Avro object container format is not required. For an example of how to serialize and deserialize data into memory buffers using reflection with the object container format when the schema must be serialized with the data, see <a href="#Scenario3">Serialization using object container files with reflection.</a>
  42. namespace Microsoft.Hadoop.Avro.Sample
  43. {
  44. using System;
  45. using System.Collections.Generic;
  46. using System.IO;
  47. using System.Linq;
  48. using System.Runtime.Serialization;
  49. using Microsoft.Hadoop.Avro.Container;
  50. //Sample Class used in serialization samples
  51. [DataContract(Name = "SensorDataValue", Namespace = "Sensors")]
  52. internal class SensorData
  53. {
  54. [DataMember(Name = "Location")]
  55. public Location Position { get; set; }
  56. [DataMember(Name = "Value")]
  57. public byte[] Value { get; set; }
  58. }
  59. //Sample struct used in serialization samples
  60. [DataContract]
  61. internal struct Location
  62. {
  63. [DataMember]
  64. public int Floor { get; set; }
  65. [DataMember]
  66. public int Room { get; set; }
  67. }
  68. //This class contains all methods demonstrating
  69. //the usage of Microsoft .NET Library for Avro
  70. public class AvroSample
  71. {
  72. //Serialize and deserialize sample data set represented as an object using Reflection
  73. //No explicit schema definition is required - schema of serialized objects is automatically built
  74. public void SerializeDeserializeObjectUsingReflection()
  75. {
  76. Console.WriteLine("SERIALIZATION USING REFLECTION\n");
  77. Console.WriteLine("Serializing Sample Data Set...");
  78. //Create a new AvroSerializer instance and specify a custom serialization strategy AvroDataContractResolver
  79. //for serializing only properties attributed with DataContract/DateMember
  80. var avroSerializer = AvroSerializer.Create<SensorData>();
  81. //Create a Memory Stream buffer
  82. using (var buffer = new MemoryStream())
  83. {
  84. //Create a data set using sample Class and struct
  85. var expected = new SensorData { Value = new byte[] { 1, 2, 3, 4, 5 }, Position = new Location { Room = 243, Floor = 1 } };
  86. //Serialize the data to the specified stream
  87. avroSerializer.Serialize(buffer, expected);
  88. Console.WriteLine("Deserializing Sample Data Set...");
  89. //Prepare the stream for deserializing the data
  90. buffer.Seek(0, SeekOrigin.Begin);
  91. //Deserialize data from the stream and cast it to the same type used for serialization
  92. var actual = avroSerializer.Deserialize(buffer);
  93. Console.WriteLine("Comparing Initial and Deserialized Data Sets...");
  94. //Finally, verify that deserialized data matches the original one
  95. bool isEqual = this.Equal(expected, actual);
  96. Console.WriteLine("Result of Data Set Identity Comparison is {0}", isEqual);
  97. }
  98. }
  99. //
  100. //Helper methods
  101. //
  102. //Comparing two SensorData objects
  103. private bool Equal(SensorData left, SensorData right)
  104. {
  105. return left.Position.Equals(right.Position) && left.Value.SequenceEqual(right.Value);
  106. }
  107. static void Main()
  108. {
  109. string sectionDivider = "---------------------------------------- ";
  110. //Create an instance of AvroSample Class and invoke methods
  111. //illustrating different serializing approaches
  112. AvroSample Sample = new AvroSample();
  113. //Serialization to memory using Reflection
  114. Sample.SerializeDeserializeObjectUsingReflection();
  115. Console.WriteLine(sectionDivider);
  116. Console.WriteLine("Press any key to exit.");
  117. Console.Read();
  118. }
  119. }
  120. }
  121. // The example is expected to display the following output:
  122. // SERIALIZATION USING REFLECTION
  123. //
  124. // Serializing Sample Data Set...
  125. // Deserializing Sample Data Set...
  126. // Comparing Initial and Deserialized Data Sets...
  127. // Result of Data Set Identity Comparison is True
  128. // ----------------------------------------
  129. // Press any key to exit.
  130. <h2> <a name="Scenario2"></a>Serialization with a generic record</h2>
  131. A JSON schema can be explicitly specified in a generic record when reflection cannot be used because the data cannot be represented using .NET classes with a data contract. This method is generally slower than using reflection and serializers for specific C# class. In such cases, the schema for the data may also be dynamic because it is not be known until compile-time. Data represented as Comma Separated Values (CSV) files whose schema is unknown until it is transformed to the Avro format at run-time is an example of this sort of dynamic scenario.
  132. This example shows how to create and use an [**AvroRecord**](http://msdn.microsoft.com/en-us/library/microsoft.hadoop.avro.avrorecord.aspx) to explicitly specify a JSON schema, how to populate it with the data, then serialize and deserialize it. The result is then compared to the initial instance to confirm that the record recovered is identical to original.
  133. The schema in this example is assumed to be shared between the readers and writers, so the Avro object container format is not required. For an example of how to serialize and deserialize data into memory buffers using a generic record with the object container format when the schema must be included with the serialized data, see <a href="#Scenario4">Serialization using object container files with generic record</a> example.
  134. namespace Microsoft.Hadoop.Avro.Sample
  135. {
  136. using System;
  137. using System.Collections.Generic;
  138. using System.IO;
  139. using System.Linq;
  140. using System.Runtime.Serialization;
  141. using Microsoft.Hadoop.Avro.Container;
  142. using Microsoft.Hadoop.Avro.Schema;
  143. //This class contains all methods demonstrating
  144. //the usage of Microsoft .NET Library for Avro
  145. public class AvroSample
  146. {
  147. //Serialize and deserialize sample data set using Generic Record.
  148. //Generic Record is a special class with the schema explicitly defined in JSON.
  149. //All serialized data should be mapped to the fields of Generic Record,
  150. //which in turn will be then serialized.
  151. public void SerializeDeserializeObjectUsingGenericRecords()
  152. {
  153. Console.WriteLine("SERIALIZATION USING GENERIC RECORD\n");
  154. Console.WriteLine("Defining the Schema and creating Sample Data Set...");
  155. //Define the schema in JSON
  156. const string Schema = @"{
  157. ""type"":""record"",
  158. ""name"":""Microsoft.Hadoop.Avro.Specifications.SensorData"",
  159. ""fields"":
  160. [
  161. {
  162. ""name"":""Location"",
  163. ""type"":
  164. {
  165. ""type"":""record"",
  166. ""name"":""Microsoft.Hadoop.Avro.Specifications.Location"",
  167. ""fields"":
  168. [
  169. { ""name"":""Floor"", ""type"":""int"" },
  170. { ""name"":""Room"", ""type"":""int"" }
  171. ]
  172. }
  173. },
  174. { ""name"":""Value"", ""type"":""bytes"" }
  175. ]
  176. }";
  177. //Create a generic serializer based on the schema
  178. var serializer = AvroSerializer.CreateGeneric(Schema);
  179. var rootSchema = serializer.WriterSchema as RecordSchema;
  180. //Create a Memory Stream buffer
  181. using (var stream = new MemoryStream())
  182. {
  183. //Create a generic record to represent the data
  184. dynamic location = new AvroRecord(rootSchema.GetField("Location").TypeSchema);
  185. location.Floor = 1;
  186. location.Room = 243;
  187. dynamic expected = new AvroRecord(serializer.WriterSchema);
  188. expected.Location = location;
  189. expected.Value = new byte[] { 1, 2, 3, 4, 5 };
  190. Console.WriteLine("Serializing Sample Data Set...");
  191. //Serialize the data
  192. serializer.Serialize(stream, expected);
  193. stream.Seek(0, SeekOrigin.Begin);
  194. Console.WriteLine("Deserializing Sample Data Set...");
  195. //Deserialize the data into a generic record
  196. dynamic actual = serializer.Deserialize(stream);
  197. Console.WriteLine("Comparing Initial and Deserialized Data Sets...");
  198. //Finally, verify the results
  199. bool isEqual = expected.Location.Floor.Equals(actual.Location.Floor);
  200. isEqual = isEqual && expected.Location.Room.Equals(actual.Location.Room);
  201. isEqual = isEqual && ((byte[])expected.Value).SequenceEqual((byte[])actual.Value);
  202. Console.WriteLine("Result of Data Set Identity Comparison is {0}", isEqual);
  203. }
  204. }
  205. static void Main()
  206. {
  207. string sectionDivider = "---------------------------------------- ";
  208. //Create an instance of AvroSample Class and invoke methods
  209. //illustrating different serializing approaches
  210. AvroSample Sample = new AvroSample();
  211. //Serialization to memory using Generic Record
  212. Sample.SerializeDeserializeObjectUsingGenericRecords();
  213. Console.WriteLine(sectionDivider);
  214. Console.WriteLine("Press any key to exit.");
  215. Console.Read();
  216. }
  217. }
  218. }
  219. // The example is expected to display the following output:
  220. // SERIALIZATION USING GENERIC RECORD
  221. //
  222. // Defining the Schema and creating Sample Data Set...
  223. // Serializing Sample Data Set...
  224. // Deserializing Sample Data Set...
  225. // Comparing Initial and Deserialized Data Sets...
  226. // Result of Data Set Identity Comparison is True
  227. // ----------------------------------------
  228. // Press any key to exit.
  229. <h2> <a name="Scenario3"></a>Serialization using object container files and serialization with reflection</h2>
  230. This example is similar to scenario in the <a href="#Scenario1"> first example</a> where the schema is implicitly specified with reflection, except that here the schema is not assumed to be known to the reader that deserializes it. The **SensorData** objects to be serialized and its implicitly specified schema are stored in an object container file represented by the [**AvroContainer**](http://msdn.microsoft.com/en-us/library/microsoft.hadoop.avro.container.avrocontainer.aspx) class.
  231. The data is serialized in this example with [**SequentialWriter<SensorData>**](http://msdn.microsoft.com/en-us/library/dn627340.aspx) and deserialized with [**SequentialReader<SensorData>**](http://msdn.microsoft.com/en-us/library/dn627340.aspx). The result then is compared to the initial instances to insure identity.
  232. The data in object container file is compressed using the default [**Deflate**][deflate-100] compression codec from .NET Framework 4.0. See the <a href="#Scenario5"> last example</a> in this topic to learn how to use a more recent and superior version of the [**Deflate**][deflate-110] compression codec available in .NET Framework 4.5.
  233. namespace Microsoft.Hadoop.Avro.Sample
  234. {
  235. using System;
  236. using System.Collections.Generic;
  237. using System.IO;
  238. using System.Linq;
  239. using System.Runtime.Serialization;
  240. using Microsoft.Hadoop.Avro.Container;
  241. //Sample Class used in serialization samples
  242. [DataContract(Name = "SensorDataValue", Namespace = "Sensors")]
  243. internal class SensorData
  244. {
  245. [DataMember(Name = "Location")]
  246. public Location Position { get; set; }
  247. [DataMember(Name = "Value")]
  248. public byte[] Value { get; set; }
  249. }
  250. //Sample struct used in serialization samples
  251. [DataContract]
  252. internal struct Location
  253. {
  254. [DataMember]
  255. public int Floor { get; set; }
  256. [DataMember]
  257. public int Room { get; set; }
  258. }
  259. //This class contains all methods demonstrating
  260. //the usage of Microsoft .NET Library for Avro
  261. public class AvroSample
  262. {
  263. //Serializes and deserializes sample data set using Reflection and Avro Object Container Files
  264. //Serialized data is compressed with Deflate codec
  265. public void SerializeDeserializeUsingObjectContainersReflection()
  266. {
  267. Console.WriteLine("SERIALIZATION USING REFLECTION AND AVRO OBJECT CONTAINER FILES\n");
  268. //Path for Avro Object Container File
  269. string path = "AvroSampleReflectionDeflate.avro";
  270. //Create a data set using sample Class and struct
  271. var testData = new List<SensorData>
  272. {
  273. new SensorData { Value = new byte[] { 1, 2, 3, 4, 5 }, Position = new Location { Room = 243, Floor = 1 } },
  274. new SensorData { Value = new byte[] { 6, 7, 8, 9 }, Position = new Location { Room = 244, Floor = 1 } }
  275. };
  276. //Serializing and saving data to file
  277. //Creating a Memory Stream buffer
  278. using (var buffer = new MemoryStream())
  279. {
  280. Console.WriteLine("Serializing Sample Data Set...");
  281. //Create a SequentialWriter instance for type SensorData which can serialize a sequence of SensorData objects to stream
  282. //Data will be compressed using Deflate codec
  283. using (var w = AvroContainer.CreateWriter<SensorData>(buffer, Codec.Deflate))
  284. {
  285. using (var writer = new SequentialWriter<SensorData>(w, 24))
  286. {
  287. // Serialize the data to stream using the sequential writer
  288. testData.ForEach(writer.Write);
  289. }
  290. }
  291. //Save stream to file
  292. Console.WriteLine("Saving serialized data to file...");
  293. if (!WriteFile(buffer, path))
  294. {
  295. Console.WriteLine("Error during file operation. Quitting method");
  296. return;
  297. }
  298. }
  299. //Reading and deserializing data
  300. //Creating a Memory Stream buffer
  301. using (var buffer = new MemoryStream())
  302. {
  303. Console.WriteLine("Reading data from file...");
  304. //Reading data from Object Container File
  305. if (!ReadFile(buffer, path))
  306. {
  307. Console.WriteLine("Error during file operation. Quitting method");
  308. return;
  309. }
  310. Console.WriteLine("Deserializing Sample Data Set...");
  311. //Prepare the stream for deserializing the data
  312. buffer.Seek(0, SeekOrigin.Begin);
  313. //Create a SequentialReader for type SensorData which will derserialize all serialized objects from the given stream
  314. //It allows iterating over the deserialized objects because it implements IEnumerable<T> interface
  315. using (var reader = new SequentialReader<SensorData>(
  316. AvroContainer.CreateReader<SensorData>(buffer, true)))
  317. {
  318. var results = reader.Objects;
  319. //Finally, verify that deserialized data matches the original one
  320. Console.WriteLine("Comparing Initial and Deserialized Data Sets...");
  321. int count = 1;
  322. var pairs = testData.Zip(results, (serialized, deserialized) => new { expected = serialized, actual = deserialized });
  323. foreach (var pair in pairs)
  324. {
  325. bool isEqual = this.Equal(pair.expected, pair.actual);
  326. Console.WriteLine("For Pair {0} result of Data Set Identity Comparison is {1}", count, isEqual);
  327. count++;
  328. }
  329. }
  330. }
  331. //Delete the file
  332. RemoveFile(path);
  333. }
  334. //
  335. //Helper methods
  336. //
  337. //Comparing two SensorData objects
  338. private bool Equal(SensorData left, SensorData right)
  339. {
  340. return left.Position.Equals(right.Position) && left.Value.SequenceEqual(right.Value);
  341. }
  342. //Saving memory stream to a new file with the given path
  343. private bool WriteFile(MemoryStream InputStream, string path)
  344. {
  345. if (!File.Exists(path))
  346. {
  347. try
  348. {
  349. using (FileStream fs = File.Create(path))
  350. {
  351. InputStream.Seek(0, SeekOrigin.Begin);
  352. InputStream.CopyTo(fs);
  353. }
  354. return true;
  355. }
  356. catch (Exception e)
  357. {
  358. Console.WriteLine("The following exception was thrown during creation and writing to the file \"{0}\"", path);
  359. Console.WriteLine(e.Message);
  360. return false;
  361. }
  362. }
  363. else
  364. {
  365. Console.WriteLine("Can not create file \"{0}\". File already exists", path);
  366. return false;
  367. }
  368. }
  369. //Reading a file content using given path to a memory stream
  370. private bool ReadFile(MemoryStream OutputStream, string path)
  371. {
  372. try
  373. {
  374. using (FileStream fs = File.Open(path, FileMode.Open))
  375. {
  376. fs.CopyTo(OutputStream);
  377. }
  378. return true;
  379. }
  380. catch (Exception e)
  381. {
  382. Console.WriteLine("The following exception was thrown during reading from the file \"{0}\"", path);
  383. Console.WriteLine(e.Message);
  384. return false;
  385. }
  386. }
  387. //Deleting file using given path
  388. private void RemoveFile(string path)
  389. {
  390. if (File.Exists(path))
  391. {
  392. try
  393. {
  394. File.Delete(path);
  395. }
  396. catch (Exception e)
  397. {
  398. Console.WriteLine("The following exception was thrown during deleting the file \"{0}\"", path);
  399. Console.WriteLine(e.Message);
  400. }
  401. }
  402. else
  403. {
  404. Console.WriteLine("Can not delete file \"{0}\". File does not exist", path);
  405. }
  406. }
  407. static void Main()
  408. {
  409. string sectionDivider = "---------------------------------------- ";
  410. //Create an instance of AvroSample Class and invoke methods
  411. //illustrating different serializing approaches
  412. AvroSample Sample = new AvroSample();
  413. //Serialization using Reflection to Avro Object Container File
  414. Sample.SerializeDeserializeUsingObjectContainersReflection();
  415. Console.WriteLine(sectionDivider);
  416. Console.WriteLine("Press any key to exit.");
  417. Console.Read();
  418. }
  419. }
  420. }
  421. // The example is expected to display the following output:
  422. // SERIALIZATION USING REFLECTION AND AVRO OBJECT CONTAINER FILES
  423. //
  424. // Serializing Sample Data Set...
  425. // Saving serialized data to file...
  426. // Reading data from file...
  427. // Deserializing Sample Data Set...
  428. // Comparing Initial and Deserialized Data Sets...
  429. // For Pair 1 result of Data Set Identity Comparison is True
  430. // For Pair 2 result of Data Set Identity Comparison is True
  431. // ----------------------------------------
  432. // Press any key to exit.
  433. <h2> <a name="Scenario4"></a>Serialization using object container files and serialization with generic record</h2>
  434. This example is similar to scenario in the <a href="#Scenario2"> second example</a> where the schema is explicitly specified with JSON, except that here the schema is not assumed to be known to the reader that deserializes it.
  435. The test data set is collected into a list of [**AvroRecord**](http://msdn.microsoft.com/en-us/library/microsoft.hadoop.avro.avrorecord.aspx) objects using an explicitly defined JSON schema and then stored in an object container file represented by the [**AvroContainer**](http://msdn.microsoft.com/en-us/library/microsoft.hadoop.avro.container.avrocontainer.aspx) class. This container file creates a writer that is used to serialize the data, uncompressed, to a memory stream that is then saved to a file. It is the [**Codex.Null**](http://msdn.microsoft.com/en-us/library/microsoft.hadoop.avro.container.codec.null.aspx) parameter used when creating the reader that specifies this data will not be compressed.
  436. The data is then read from the file and deserialized into a collection of objects. This collection is compared to the initial list of Avro records to confirm that they are identical.
  437. namespace Microsoft.Hadoop.Avro.Sample
  438. {
  439. using System;
  440. using System.Collections.Generic;
  441. using System.IO;
  442. using System.Linq;
  443. using System.Runtime.Serialization;
  444. using Microsoft.Hadoop.Avro.Container;
  445. using Microsoft.Hadoop.Avro.Schema;
  446. //This class contains all methods demonstrating
  447. //the usage of Microsoft .NET Library for Avro
  448. public class AvroSample
  449. {
  450. //Serializes and deserializes sample data set using Generic Record and Avro Object Container Files
  451. //Serialized data is not compressed
  452. public void SerializeDeserializeUsingObjectContainersGenericRecord()
  453. {
  454. Console.WriteLine("SERIALIZATION USING GENERIC RECORD AND AVRO OBJECT CONTAINER FILES\n");
  455. //Path for Avro Object Container File
  456. string path = "AvroSampleGenericRecordNullCodec.avro";
  457. Console.WriteLine("Defining the Schema and creating Sample Data Set...");
  458. //Define the schema in JSON
  459. const string Schema = @"{
  460. ""type"":""record"",
  461. ""name"":""Microsoft.Hadoop.Avro.Specifications.SensorData"",
  462. ""fields"":
  463. [
  464. {
  465. ""name"":""Location"",
  466. ""type"":
  467. {
  468. ""type"":""record"",
  469. ""name"":""Microsoft.Hadoop.Avro.Specifications.Location"",
  470. ""fields"":
  471. [
  472. { ""name"":""Floor"", ""type"":""int"" },
  473. { ""name"":""Room"", ""type"":""int"" }
  474. ]
  475. }
  476. },
  477. { ""name"":""Value"", ""type"":""bytes"" }
  478. ]
  479. }";
  480. //Create a generic serializer based on the schema
  481. var serializer = AvroSerializer.CreateGeneric(Schema);
  482. var rootSchema = serializer.WriterSchema as RecordSchema;
  483. //Create a generic record to represent the data
  484. var testData = new List<AvroRecord>();
  485. dynamic expected1 = new AvroRecord(rootSchema);
  486. dynamic location1 = new AvroRecord(rootSchema.GetField("Location").TypeSchema);
  487. location1.Floor = 1;
  488. location1.Room = 243;
  489. expected1.Location = location1;
  490. expected1.Value = new byte[] { 1, 2, 3, 4, 5 };
  491. testData.Add(expected1);
  492. dynamic expected2 = new AvroRecord(rootSchema);
  493. dynamic location2 = new AvroRecord(rootSchema.GetField("Location").TypeSchema);
  494. location2.Floor = 1;
  495. location2.Room = 244;
  496. expected2.Location = location2;
  497. expected2.Value = new byte[] { 6, 7, 8, 9 };
  498. testData.Add(expected2);
  499. //Serializing and saving data to file
  500. //Create a MemoryStream buffer
  501. using (var buffer = new MemoryStream())
  502. {
  503. Console.WriteLine("Serializing Sample Data Set...");
  504. //Create a SequentialWriter instance for type SensorData which can serialize a sequence of SensorData objects to stream
  505. //Data will not be compressed (Null compression codec)
  506. using (var writer = AvroContainer.CreateGenericWriter(Schema, buffer, Codec.Null))
  507. {
  508. using (var streamWriter = new SequentialWriter<object>(writer, 24))
  509. {
  510. // Serialize the data to stream using the sequential writer
  511. testData.ForEach(streamWriter.Write);
  512. }
  513. }
  514. Console.WriteLine("Saving serialized data to file...");
  515. //Save stream to file
  516. if (!WriteFile(buffer, path))
  517. {
  518. Console.WriteLine("Error during file operation. Quitting method");
  519. return;
  520. }
  521. }
  522. //Reading and deserializng the data
  523. //Create a Memory Stream buffer
  524. using (var buffer = new MemoryStream())
  525. {
  526. Console.WriteLine("Reading data from file...");
  527. //Reading data from Object Container File
  528. if (!ReadFile(buffer, path))
  529. {
  530. Console.WriteLine("Error during file operation. Quitting method");
  531. return;
  532. }
  533. Console.WriteLine("Deserializing Sample Data Set...");
  534. //Prepare the stream for deserializing the data
  535. buffer.Seek(0, SeekOrigin.Begin);
  536. //Create a SequentialReader for type SensorData which will derserialize all serialized objects from the given stream
  537. //It allows iterating over the deserialized objects because it implements IEnumerable<T> interface
  538. using (var reader = AvroContainer.CreateGenericReader(buffer))
  539. {
  540. using (var streamReader = new SequentialReader<object>(reader))
  541. {
  542. var results = streamReader.Objects;
  543. Console.WriteLine("Comparing Initial and Deserialized Data Sets...");
  544. //Finally, verify the results
  545. var pairs = testData.Zip(results, (serialized, deserialized) => new { expected = (dynamic)serialized, actual = (dynamic)deserialized });
  546. int count = 1;
  547. foreach (var pair in pairs)
  548. {
  549. bool isEqual = pair.expected.Location.Floor.Equals(pair.actual.Location.Floor);
  550. isEqual = isEqual && pair.expected.Location.Room.Equals(pair.actual.Location.Room);
  551. isEqual = isEqual && ((byte[])pair.expected.Value).SequenceEqual((byte[])pair.actual.Value);
  552. Console.WriteLine("For Pair {0} result of Data Set Identity Comparison is {1}", count, isEqual.ToString());
  553. count++;
  554. }
  555. }
  556. }
  557. }
  558. //Delete the file
  559. RemoveFile(path);
  560. }
  561. //
  562. //Helper methods
  563. //
  564. //Saving memory stream to a new file with the given path
  565. private bool WriteFile(MemoryStream InputStream, string path)
  566. {
  567. if (!File.Exists(path))
  568. {
  569. try
  570. {
  571. using (FileStream fs = File.Create(path))
  572. {
  573. InputStream.Seek(0, SeekOrigin.Begin);
  574. InputStream.CopyTo(fs);
  575. }
  576. return true;
  577. }
  578. catch (Exception e)
  579. {
  580. Console.WriteLine("The following exception was thrown during creation and writing to the file \"{0}\"", path);
  581. Console.WriteLine(e.Message);
  582. return false;
  583. }
  584. }
  585. else
  586. {
  587. Console.WriteLine("Can not create file \"{0}\". File already exists", path);
  588. return false;
  589. }
  590. }
  591. //Reading a file content using given path to a memory stream
  592. private bool ReadFile(MemoryStream OutputStream, string path)
  593. {
  594. try
  595. {
  596. using (FileStream fs = File.Open(path, FileMode.Open))
  597. {
  598. fs.CopyTo(OutputStream);
  599. }
  600. return true;
  601. }
  602. catch (Exception e)
  603. {
  604. Console.WriteLine("The following exception was thrown during reading from the file \"{0}\"", path);
  605. Console.WriteLine(e.Message);
  606. return false;
  607. }
  608. }
  609. //Deleting file using given path
  610. private void RemoveFile(string path)
  611. {
  612. if (File.Exists(path))
  613. {
  614. try
  615. {
  616. File.Delete(path);
  617. }
  618. catch (Exception e)
  619. {
  620. Console.WriteLine("The following exception was thrown during deleting the file \"{0}\"", path);
  621. Console.WriteLine(e.Message);
  622. }
  623. }
  624. else
  625. {
  626. Console.WriteLine("Can not delete file \"{0}\". File does not exist", path);
  627. }
  628. }
  629. static void Main()
  630. {
  631. string sectionDivider = "---------------------------------------- ";
  632. //Create an instance of AvroSample Class and invoke methods
  633. //illustrating different serializing approaches
  634. AvroSample Sample = new AvroSample();
  635. //Serialization using Generic Record to Avro Object Container File
  636. Sample.SerializeDeserializeUsingObjectContainersGenericRecord();
  637. Console.WriteLine(sectionDivider);
  638. Console.WriteLine("Press any key to exit.");
  639. Console.Read();
  640. }
  641. }
  642. }
  643. // The example is expected to display the following output:
  644. // SERIALIZATION USING GENERIC RECORD AND AVRO OBJECT CONTAINER FILES
  645. //
  646. // Defining the Schema and creating Sample Data Set...
  647. // Serializing Sample Data Set...
  648. // Saving serialized data to file...
  649. // Reading data from file...
  650. // Deserializing Sample Data Set...
  651. // Comparing Initial and Deserialized Data Sets...
  652. // For Pair 1 result of Data Set Identity Comparison is True
  653. // For Pair 2 result of Data Set Identity Comparison is True
  654. // ----------------------------------------
  655. // Press any key to exit.
  656. <h2> <a name="Scenario5"></a>Serialization using object container files with a custom compression codec</h2>
  657. The example below shows how to use a custom compression codec for Avro object container files. The [Avro Specification](http://avro.apache.org/docs/current/spec.html#Required+Codecs) allows usage of an optional compression codec (in addition to **Null** and **Deflate** defaults). This example is not implementing completely new codec such Snappy (mentioned as a supported optional codec in [Avro Specification](http://avro.apache.org/docs/current/spec.html#snappy)). It shows how to use the .NET Framework 4.5 implementation of the [**Deflate**][deflate-110] codec which provides a better compression algorithm based on the [zlib](http://zlib.net/) compression library than the default .NET Framework 4.0 version.
  658. //
  659. // This code needs to be compiled with the parameter Target Framework set as ".NET Framework 4.5"
  660. // to ensure the desired implementation of Deflate compression algorithm is used
  661. // Ensure your C# Project is set up accordingly
  662. //
  663. namespace Microsoft.Hadoop.Avro.Sample
  664. {
  665. using System;
  666. using System.Collections.Generic;
  667. using System.Diagnostics;
  668. using System.IO;
  669. using System.IO.Compression;
  670. using System.Linq;
  671. using System.Runtime.Serialization;
  672. using Microsoft.Hadoop.Avro.Container;
  673. #region Defining objects for serialization
  674. //Sample Class used in serialization samples
  675. [DataContract(Name = "SensorDataValue", Namespace = "Sensors")]
  676. internal class SensorData
  677. {
  678. [DataMember(Name = "Location")]
  679. public Location Position { get; set; }
  680. [DataMember(Name = "Value")]
  681. public byte[] Value { get; set; }
  682. }
  683. //Sample struct used in serialization samples
  684. [DataContract]
  685. internal struct Location
  686. {
  687. [DataMember]
  688. public int Floor { get; set; }
  689. [DataMember]
  690. public int Room { get; set; }
  691. }
  692. #endregion
  693. #region Defining custom codec based on .NET Framework V.4.5 Deflate
  694. //Avro.NET Codec class contains two methods
  695. //GetCompressedStreamOver(Stream uncompressed) and GetDecompressedStreamOver(Stream compressed)
  696. //which are the key ones for data compression.
  697. //To enable a custom codec one needs to implement these methods for the required codec
  698. #region Defining Compression and Decompression Streams
  699. //DeflateStream (class from System.IO.Compression namespace that implements Deflate algorithm)
  700. //can not be directly used for Avro because it does not support vital operations like Seek.
  701. //Thus one needs to implement two classes inherited from Stream
  702. //(one for compressed and one for decompressed stream)
  703. //that use Deflate compression and implement all required features
  704. internal sealed class CompressionStreamDeflate45 : Stream
  705. {
  706. private readonly Stream buffer;
  707. private DeflateStream compressionStream;
  708. public CompressionStreamDeflate45(Stream buffer)
  709. {
  710. Debug.Assert(buffer != null, "Buffer is not allowed to be null.");
  711. this.compressionStream = new DeflateStream(buffer, CompressionLevel.Fastest, true);
  712. this.buffer = buffer;
  713. }
  714. public override bool CanRead
  715. {
  716. get { return this.buffer.CanRead; }
  717. }
  718. public override bool CanSeek
  719. {
  720. get { return true; }
  721. }
  722. public override bool CanWrite
  723. {
  724. get { return this.buffer.CanWrite; }
  725. }
  726. public override void Flush()
  727. {
  728. this.compressionStream.Close();
  729. }
  730. public override long Length
  731. {
  732. get { return this.buffer.Length; }
  733. }
  734. public override long Position
  735. {
  736. get
  737. {
  738. return this.buffer.Position;
  739. }
  740. set
  741. {
  742. this.buffer.Position = value;
  743. }
  744. }
  745. public override int Read(byte[] buffer, int offset, int count)
  746. {
  747. return this.buffer.Read(buffer, offset, count);
  748. }
  749. public override long Seek(long offset, SeekOrigin origin)
  750. {
  751. return this.buffer.Seek(offset, origin);
  752. }
  753. public override void SetLength(long value)
  754. {
  755. throw new NotSupportedException();
  756. }
  757. public override void Write(byte[] buffer, int offset, int count)
  758. {
  759. this.compressionStream.Write(buffer, offset, count);
  760. }
  761. protected override void Dispose(bool disposed)
  762. {
  763. base.Dispose(disposed);
  764. if (disposed)
  765. {
  766. this.compressionStream.Dispose();
  767. this.compressionStream = null;
  768. }
  769. }
  770. }
  771. internal sealed class DecompressionStreamDeflate45 : Stream
  772. {
  773. private readonly DeflateStream decompressed;
  774. public DecompressionStreamDeflate45(Stream compressed)
  775. {
  776. this.decompressed = new DeflateStream(compressed, CompressionMode.Decompress, true);
  777. }
  778. public override bool CanRead
  779. {
  780. get { return true; }
  781. }
  782. public override bool CanSeek
  783. {
  784. get { return true; }
  785. }
  786. public override bool CanWrite
  787. {
  788. get { return false; }
  789. }
  790. public override void Flush()
  791. {
  792. this.decompressed.Close();
  793. }
  794. public override long Length
  795. {
  796. get { return this.decompressed.Length; }
  797. }
  798. public override long Position
  799. {
  800. get
  801. {
  802. return this.decompressed.Position;
  803. }
  804. set
  805. {
  806. throw new NotSupportedException();
  807. }
  808. }
  809. public override int Read(byte[] buffer, int offset, int count)
  810. {
  811. return this.decompressed.Read(buffer, offset, count);
  812. }
  813. public override long Seek(long offset, SeekOrigin origin)
  814. {
  815. throw new NotSupportedException();
  816. }
  817. public override void SetLength(long value)
  818. {
  819. throw new NotSupportedException();
  820. }
  821. public override void Write(byte[] buffer, int offset, int count)
  822. {
  823. throw new NotSupportedException();
  824. }
  825. protected override void Dispose(bool disposing)
  826. {
  827. base.Dispose(disposing);
  828. if (disposing)
  829. {
  830. this.decompressed.Dispose();
  831. }
  832. }
  833. }
  834. #endregion
  835. #region Define Codec
  836. //Define the actual codec class containing the required methods for manipulating streams:
  837. //GetCompressedStreamOver(Stream uncompressed) and GetDecompressedStreamOver(Stream compressed)
  838. //Codec class uses classes for comressed and decompressed streams defined above
  839. internal sealed class DeflateCodec45 : Codec
  840. {
  841. //We merely use different IMPLEMENTATION of Deflate, so the CodecName remains "deflate"
  842. public static readonly string CodecName = "deflate";
  843. public DeflateCodec45()
  844. : base(CodecName)
  845. {
  846. }
  847. public override Stream GetCompressedStreamOver(Stream decompressed)
  848. {
  849. if (decompressed == null)
  850. {
  851. throw new ArgumentNullException("decompressed");
  852. }
  853. return new CompressionStreamDeflate45(decompressed);
  854. }
  855. public override Stream GetDecompressedStreamOver(Stream compressed)
  856. {
  857. if (compressed == null)
  858. {
  859. throw new ArgumentNullException("compressed");
  860. }
  861. return new DecompressionStreamDeflate45(compressed);
  862. }
  863. }
  864. #endregion
  865. #region Define modified Codec Factory
  866. //Define modified Codec Factory to be used in Reader
  867. //It will catch the attempt to use "deflate" and provide Custom Codec
  868. //For all other cases it will rely on the base class (CodecFactory)
  869. internal sealed class CodecFactoryDeflate45 : CodecFactory
  870. {
  871. public override Codec Create(string codecName)
  872. {
  873. if (codecName == DeflateCodec45.CodecName)
  874. return new DeflateCodec45();
  875. else
  876. return base.Create(codecName);
  877. }
  878. }
  879. #endregion
  880. #endregion
  881. #region Sample Class with demonstration methods
  882. //This class contains methods demonstrating
  883. //the usage of Microsoft .NET Library for Avro
  884. public class AvroSample
  885. {
  886. //Serializes and deserializes sample data set using Reflection and Avro Object Container Files
  887. //Serialized data is compressed with the Custom compression codec (Deflate of .NET Framework 4.5)
  888. //
  889. //This sample uses Memory Stream for all operations related to serialization, deserialization and
  890. //Object Container manipulation, though File Stream could be easily used.
  891. public void SerializeDeserializeUsingObjectContainersReflectionCustomCodec()
  892. {
  893. Console.WriteLine("SERIALIZATION USING REFLECTION, AVRO OBJECT CONTAINER FILES AND CUSTOM CODEC\n");
  894. //Path for Avro Object Container File
  895. string path = "AvroSampleReflectionDeflate45.avro";
  896. //Create a data set using sample Class and struct
  897. var testData = new List<SensorData>
  898. {
  899. new SensorData { Value = new byte[] { 1, 2, 3, 4, 5 }, Position = new Location { Room = 243, Floor = 1 } },
  900. new SensorData { Value = new byte[] { 6, 7, 8, 9 }, Position = new Location { Room = 244, Floor = 1 } }
  901. };
  902. //Serializing and saving data to file
  903. //Creating a Memory Stream buffer
  904. using (var buffer = new MemoryStream())
  905. {
  906. Console.WriteLine("Serializing Sample Data Set...");
  907. //Create a SequentialWriter instance for type SensorData which can serialize a sequence of SensorData objects to stream
  908. //Here the custom Codec is introduced. For convenience the next commented code line shows how to use built-in Deflate.
  909. //Note, that because the sample deals with different IMPLEMENTATIONS of Deflate, built-in and custom codecs are interchangeable
  910. //in read-write operations
  911. //using (var w = AvroContainer.CreateWriter<SensorData>(buffer, Codec.Deflate))
  912. using (var w = AvroContainer.CreateWriter<SensorData>(buffer, new DeflateCodec45()))
  913. {
  914. using (var writer = new SequentialWriter<SensorData>(w, 24))
  915. {
  916. // Serialize the data to stream using the sequential writer
  917. testData.ForEach(writer.Write);
  918. }
  919. }
  920. //Save stream to file
  921. Console.WriteLine("Saving serialized data to file...");
  922. if (!WriteFile(buffer, path))
  923. {
  924. Console.WriteLine("Error during file operation. Quitting method");
  925. return;
  926. }
  927. }
  928. //Reading and deserializing data
  929. //Creating a Memory Stream buffer
  930. using (var buffer = new MemoryStream())
  931. {
  932. Console.WriteLine("Reading data from file...");
  933. //Reading data from Object Container File
  934. if (!ReadFile(buffer, path))
  935. {
  936. Console.WriteLine("Error during file operation. Quitting method");
  937. return;
  938. }
  939. Console.WriteLine("Deserializing Sample Data Set...");
  940. //Prepare the stream for deserializing the data
  941. buffer.Seek(0, SeekOrigin.Begin);
  942. //Because of SequentialReader<T> constructor signature an AvroSerializerSettings instance is required
  943. //when Codec Factory is explicitly specified
  944. //You may comment the line below if you want to use built-in Deflate (see next comment)
  945. AvroSerializerSettings settings = new AvroSerializerSettings();
  946. //Create a SequentialReader for type SensorData which will derserialize all serialized objects from the given stream
  947. //It allows iterating over the deserialized objects because it implements IEnumerable<T> interface
  948. //Here the custom Codec Factory is introduced.
  949. //For convenience the next commented code line shows how to use built-in Deflate
  950. //(no explicit Codec Factory parameter is required in this case).
  951. //Note, that because the sample deals with different IMPLEMENTATIONS of Deflate, built-in and custom codecs are interchangeable
  952. //in read-write operations
  953. //using (var reader = new SequentialReader<SensorData>(AvroContainer.CreateReader<SensorData>(buffer, true)))
  954. using (var reader = new SequentialReader<SensorData>(
  955. AvroContainer.CreateReader<SensorData>(buffer, true, settings, new CodecFactoryDeflate45())))
  956. {
  957. var results = reader.Objects;
  958. //Finally, verify that deserialized data matches the original one
  959. Console.WriteLine("Comparing Initial and Deserialized Data Sets...");
  960. bool isEqual;
  961. int count = 1;
  962. var pairs = testData.Zip(results, (serialized, deserialized) => new { expected = serialized, actual = deserialized });
  963. foreach (var pair in pairs)
  964. {
  965. isEqual = this.Equal(pair.expected, pair.actual);
  966. Console.WriteLine("For Pair {0} result of Data Set Identity Comparison is {1}", count, isEqual.ToString());
  967. count++;
  968. }
  969. }
  970. }
  971. //Delete the file
  972. RemoveFile(path);
  973. }
  974. #endregion
  975. #region Helper Methods
  976. //Comparing two SensorData objects
  977. private bool Equal(SensorData left, SensorData right)
  978. {
  979. return left.Position.Equals(right.Position) && left.Value.SequenceEqual(right.Value);
  980. }
  981. //Saving memory stream to a new file with the given path
  982. private bool WriteFile(MemoryStream InputStream, string path)
  983. {
  984. if (!File.Exists(path))
  985. {
  986. try
  987. {
  988. using (FileStream fs = File.Create(path))
  989. {
  990. InputStream.Seek(0, SeekOrigin.Begin);
  991. InputStream.CopyTo(fs);
  992. }
  993. return true;
  994. }
  995. catch (Exception e)
  996. {
  997. Console.WriteLine("The following exception was thrown during creation and writing to the file \"{0}\"", path);
  998. Console.WriteLine(e.Message);
  999. return false;
  1000. }
  1001. }
  1002. else
  1003. {
  1004. Console.WriteLine("Can not create file \"{0}\". File already exists", path);
  1005. return false;
  1006. }
  1007. }
  1008. //Reading a file content using given path to a memory stream
  1009. private bool ReadFile(MemoryStream OutputStream, string path)
  1010. {
  1011. try
  1012. {
  1013. using (FileStream fs = File.Open(path, FileMode.Open))
  1014. {
  1015. fs.CopyTo(OutputStream);
  1016. }
  1017. return true;
  1018. }
  1019. catch (Exception e)
  1020. {
  1021. Console.WriteLine("The following exception was thrown during reading from the file \"{0}\"", path);
  1022. Console.WriteLine(e.Message);
  1023. return false;
  1024. }
  1025. }
  1026. //Deleting file using given path
  1027. private void RemoveFile(string path)
  1028. {
  1029. if (File.Exists(path))
  1030. {
  1031. try
  1032. {
  1033. File.Delete(path);
  1034. }
  1035. catch (Exception e)
  1036. {
  1037. Console.WriteLine("The following exception was thrown during deleting the file \"{0}\"", path);
  1038. Console.WriteLine(e.Message);
  1039. }
  1040. }
  1041. else
  1042. {
  1043. Console.WriteLine("Can not delete file \"{0}\". File does not exist", path);
  1044. }
  1045. }
  1046. #endregion
  1047. static void Main()
  1048. {
  1049. string sectionDivider = "---------------------------------------- ";
  1050. //Create an instance of AvroSample Class and invoke methods
  1051. //illustrating different serializing approaches
  1052. AvroSample Sample = new AvroSample();
  1053. //Serialization using Reflection to Avro Object Container File using Custom Codec
  1054. Sample.SerializeDeserializeUsingObjectContainersReflectionCustomCodec();
  1055. Console.WriteLine(sectionDivider);
  1056. Console.WriteLine("Press any key to exit.");
  1057. Console.Read();
  1058. }
  1059. }
  1060. }
  1061. // The example is expected to display the following output:
  1062. // SERIALIZATION USING REFLECTION, AVRO OBJECT CONTAINER FILES AND CUSTOM CODEC
  1063. //
  1064. // Serializing Sample Data Set...
  1065. // Saving serialized data to file...
  1066. // Reading data from file...
  1067. // Deserializing Sample Data Set...
  1068. // Comparing Initial and Deserialized Data Sets...
  1069. // For Pair 1 result of Data Set Identity Comparison is True
  1070. //For Pair 2 result of Data Set Identity Comparison is True
  1071. // ----------------------------------------
  1072. // Press any key to exit.
  1073. [deflate-100]: http://msdn.microsoft.com/en-us/library/system.io.compression.deflatestream(v=vs.100).aspx
  1074. [deflate-110]: http://msdn.microsoft.com/en-us/library/system.io.compression.deflatestream(v=vs.110).aspx