Data formats are an essential part of any programming toolbox. C# developers must understand how to take plain-old-C#-objects (POCOs) and convert them into standard data formats. The .NET community commonly refers to the conversion of data objects as serialization.
In this post, I will show you how to take a simple POCO and serialize it into the following formats:
- JavaScript Object Notation (JSON)
- Extensible Markup Language (XML)
- YAML Ain’t Markup Language (YAML)
- Comma Separated Values (CSV)
- Tom’s Obvious, Minimal Language (TOML)
This post is for beginners starting in C# development to seasoned developers needing a quick refresher on how to serialize data. While this post does not contain all the formats you could convert a POCO to, these are the formats I commonly run into when developing most of my applications.
A note before we get started, the examples you will see all write our objects to a string
value in memory. Serializing in memory is the most flexible approach I’ve found, as it usually works for console applications, web applications, and most .NET use cases. That said, it may not be right for you. Please consider your use case before copying any of the following code, and be prepared to make modifications to meet your needs.
Also, all examples are implemented inside a class so that I can iterate over each format. The code you’ll want to pay attention to will be inside the RunAsync
method of the IRun
interface. Additionally, we’ll be serializing a Friend
POCO in each example with varying data types.
var friend = new Friend
{
Id = 1,
Debt = 10m,
Met = DateTime.Now,
Name = "Khalid Abuhakmeh"
};
If you’d like to skip directly to the code, you can download the sample project at GitHub.
Serialize Object To JSON
What Is JSON?
JavaScript object notation is a structure characterized by curly braces ({
and }
), name-value pairs, and arrays. JSON is a format commonly used on the web. The format is primarily used in the frontend of many applications within JavaScript code, served by backend APIs for client consumption, and transmitted between APIs.
JSON was created with two main goals in mind:
It is easy for humans to read and write. It is easy for machines to parse and generate.
While the format itself is language-agnostic, it is inspired heavily by C based language like C, JavaScript, and C#. If you want to understand the specifications of this format fully, I recommend going to JSON.org.
When Should I Use JSON?
JSON is ubiquitous, and I would bet all modern programming languages support it. Here are a few cases where you should consider JSON as an option for data interchange:
You are building a web-based API
You are developing a JavaScript application
You want a human-readable format
As a .NET developer, many of .NET’s frameworks support JSON, and recently in ASP.NET Core 3, .NET developers received a new JsonSerializer
in the System.Text.Json
namespace.
The similarities between a POCO and JSON make it an ideal match since most JSON results will look very similar to C# definitions as you’ll see in the example below.
JSON.NET Serialization
So you’re ready to serialize a C# object to JSON. In .NET, you have two distinct choices, which I will show you how to use. The first is JSON.NET
and is your best choice today.
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace conversions
{
public class JsonNet : IRun
{
public string Name => "Json.NET";
public Task RunAsync<T>(T input)
{
var result = JsonConvert.SerializeObject(input);
Console.WriteLine(result);
return Task.CompletedTask;
}
}
}
As you can see, JSON.NET
handles most of the ceremony around serialization for you. You give it a type, and it can convert it directly in a string
. The resulting output of our serialization is as follows.
{"Id":1,"Name":"Khalid Abuhakmeh","Met":"2019-10-02T21:45:17.895679-04:00","Debt":10.0}
Note, you can alter the output by following the JSON.NET documentation.
System.Text.Json Serialization
With the release of .NET Core 3, the community now has access to a built-in JsonSerializer
developed by the same developer who developed JSON.NET, James Newton King. It has many similarities with JSON.NET and is built from the ground up with performance in mind. It is less configurable than JSON.NET and currently doesn’t handle some of the same edge cases that mature library does.
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
namespace conversions
{
public class Json : IRun
{
public string Name => "System.Text.Json";
public async Task RunAsync<T>(T input)
{
var stream = new MemoryStream();
await JsonSerializer.SerializeAsync(stream, input);
// reset the stream back to 0
stream.Position = 0;
using var reader = new StreamReader(stream);
// we reread the stream to a string
var json = await reader.ReadToEndAsync();
Console.WriteLine(json);
}
}
}
Running the code above we get the following result:
{"Id":1,"Name":"Khalid Abuhakmeh","Met":"2019-10-02T21:45:17.895679-04:00","Debt":10}
The one thing I noticed is that the decimal value for Debt
is handled differently than the default configuration for JSON.NET as it drops the .0
from the decimal value of 10
.
The above example serializes to a memory stream, but you can also just utilize the Serialize
method on JsonSerializer
for a simpler code base.
public class JsonSimple : IRun
{
public string Name => "System.Text.Json Simple";
public Task RunAsync<T>(T input)
{
var result = JsonSerializer.Serialize(input);
Console.WriteLine(result);
return Task.CompletedTask;
}
}
Note that the Serialize
is not async
so we have to return Task.CompletedTask
from our method.
Serialize Object To XML
What Is XML?
The Extensible Markup Language, also known as XML, is the original gangster of data formats (at least when it comes to the web). It is a format that utilizes tags, attributes, and schemas to create highly structured and dense results. Like JSON, XML was designed to be readable by humans and machines.
The design goals of XML emphasize simplicity, generality, and usability across the Internet. –Wikipedia
When Should I Use XML?
XML offers more options for encapsulating data and can be more efficient as a data format. Before JSON took over the web, XML was the defacto way of transferring information between backends and frontends. Like JSON, XML enjoys broad support in the .NET ecosystem with an XmlSerializer
being available in the using System.Xml.Serialization
namespace.
XML Serialization
This example will look very similar to JSON serialization, but note that we need to create the XmlSerializer
for the specific type we are serializing.
using System;
using System.IO;
using System.Text;
using System.Text.Unicode;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;
namespace conversions
{
public class Xml : IRun
{
public string Name => nameof(Xml);
public async Task RunAsync<T>(T input)
{
// if you can, only create one serializer
// creating serializers is an expensive
// operation and can be slow
var serializer
= new XmlSerializer(typeof(T));
// we will write the our result to memory
await using var stream = new MemoryStream();
// the string will be utf8 encoded
await using var writer = new StreamWriter(stream, Encoding.UTF8);
// here we go!
serializer.Serialize(writer, input);
// flush our write to make sure its all there
await writer.FlushAsync();
// reset the stream back to 0
stream.Position = 0;
using var reader = new StreamReader(stream);
// we reread the stream to a string
var xml = await reader.ReadToEndAsync();
Console.Write($"{xml}\n");
}
}
}
Creating XML Serializers in a .NET application can be expensive. Cache these serializers when you can. It will give you a significant performance boost. Here is our result.
<?xml version="1.0" encoding="utf-8"?>
<Friend xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Id>1</Id>
<Name>Khalid Abuhakmeh</Name>
<Met>2019-10-02T21:45:17.895679-04:00</Met>
<Debt>10</Debt>
</Friend>
The example I have above is a simple XML serialization approach, but with .NET, we can decorate are classes to appropriate put values in tags or attributes.
Serialize Object To YAML
What Is YAML?
YAML is a format defined by key-value pairs and an emphasis on whitespace. Described by the official site as:
YAML is a human-friendly data serialization standard for all programming languages. –YAML.org
Its simplicity can be refreshing, but it can also be infuriating when spaces and tabs cause subtle errors.
When Should I Use YAML?
I recommend using YAML when generating configuration files for services like Azure DevOps. Otherwise, I usually steer clear of this format.
YAML Serialization
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
namespace conversions
{
public class Yaml : IRun
{
public string Name => nameof(Yaml);
public async Task RunAsync<T>(T input)
{
var serializer = new Serializer();
var yaml = new StringBuilder();
await using var textWriter = new StringWriter(yaml);
serializer.Serialize(textWriter, input, typeof(T));
Console.WriteLine(yaml.ToString());
}
}
}
There you have it, converting a C# object to YAML.
Serialize Object To CSV
What is a CSV?
Comma Separated Values, commonly known as CSV files, are simple text files where a character separates values. In most cases, that separator is a ,
but it can be any special character, such as a |
or even a tab.
When Should I Use CSV?
Comma Separated Values can be helpful for your end users that need their data exported from the application you are building. It is very human-readable, and many spreadsheet applications support it. Not only is it an excellent format for exporting data, but it can also be even better for importing data into a system.
CSV Serialization
In my example, I use a great NuGet package called CsvHelper. While there are other ways to parse CSV files, I find this particular package battle-hardened and fully featured. Let’s take a look at my example.
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using CsvHelper;
namespace conversions
{
public class Csv : IRun
{
public string Name => "Csv";
public async Task RunAsync<T>(T input)
{
var csv = new StringBuilder();
await using var textWriter = new StringWriter(csv);
using var csvWriter = new CsvWriter(textWriter);
// automatically map the properties of T
csvWriter.Configuration.AutoMap<T>();
// we want to have a header row (optional)
csvWriter.WriteHeader<T>();
await csvWriter.NextRecordAsync();
// write our record
csvWriter.WriteRecord(input);
// make sure all records are flushed to stream
await csvWriter.FlushAsync();
Console.WriteLine(csv.ToString());
}
}
}
We get the following output.
Id,Name,Met,Debt
1,Khalid Abuhakmeh,10/05/2019 17:01:04,10
Serialize Object To TOML
What is TOML?
TOML is a relatively new file format, that takes inspiration from YAML, but has attempted to fix the issues with whitespace significance.
TOML aims to be a minimal configuration file format that’s easy to read due to obvious semantics. TOML is designed to map unambiguously to a hash table. TOML should be easy to parse into data structures in a wide variety of languages. –TOML
When Should I Use TOML?
Frankly, this is an excellent format for reading information, but I wouldn’t suggest using it as a data transfer format. JSON or XML is a better fit for most use-cases, but if you still want to use this format, it is extremely capable of supporting many data types.
TOML Serialization
I ended up using a library called Nett
for my example. It seems fully featured and capable of most TOML serialization.
using System;
using System.Threading.Tasks;
using Nett;
namespace conversions
{
public class Toml : IRun
{
public string Name => nameof(Toml);
public Task RunAsync<T>(T input)
{
var settings = TomlSettings.Create(cfg => cfg
.ConfigureType<decimal>(ct => ct
.WithConversionFor<TomlString>(conv => conv
.ToToml(s => s.ToString("C")))));
Console.WriteLine(Nett.Toml.WriteString(input, settings));
return Task.CompletedTask;
}
}
}
Note that the previous code had to account for our decimal
property. The Nett
library supports any conversion through the TomlSettings
class. The resulting output of our run is as expected.
Id = 1
Name = "Khalid Abuhakmeh"
Met = 2019-10-05 17:01:04.823084-04:00
Debt = "¤10.00"
Conclusion
After reading this post, you should have a bevy of data format options for your application. Anyone of the examples above should get you a working serialization prototype. Remember that you should consider your specific use case and whether each sample needs to be tweaked.
As you can tell, looking through the samples, many of the approaches are quite similar. You take a C# object, serialize it into a Stream
or directly into a String
value. Some libraries will do much of the heavy lifting for you, while others give you low-level interfaces you need to put together.
If you can think of any other formats you use to serialize your data, I would love to hear about it. This project is on GitHub, so also feel free to submit your serialization examples.