Make a deep copy of a C# object instance with JSON serialization
In this post, I demonstrate reference types and how to make a deep copy of a reference type using JSON serialization.
In this post, I will discuss a method for converting a list of C# objects to a CSV file. This approach can be used to implement export to CSV functionality.
Converting a C# object into another format is called serialization. The .NET base class library includes the System.Text.Json
namespace. It provides classes that make it easy to work with JSON files, including serialization and deserialization. Deserialization is the opposite of serialization, which converts a JSON object into a .NET object. In this article, we focus on serialization.
Our demo project will create a CSV of the top-paying technology jobs based on this article on indeed.com: The Top 25 Highest-Paying Technology Jobs and Their Duties. We will only model our data for the top three highest-paying tech jobs.
Here is our Job
class:
Next we will create a list of jobs.
Now that we have our demo data, it’s time to serialize our topThreeJobs
object. We can use the built-in System.Text.Json
JsonSerializer
class to accomplish this task.
I passed the optional options parameter to make the printed JSON more readable. If we write the serializedTopThreeJobs
variable to the console, you will see that we have JSON!
Now, we need to convert our new JSON data to a CSV. Since we have a JSON array, we must find a way to iterate the JSON array and write the list of property values as a row in our CSV file. We can use JsonSerializer
for this too. However, Instead of calling the Serialize
method on the JsonSerializer
class, let’s call the SerializeToDocument
, which will convert our C# object to a JsonDocument
, which will allow us to examine the JSON programmatically. Additionally, SerializerToDocument
implements IDisposable
, so we assign our variable with using
a declaration.
A JsonDocument is composed of JsonElements and each JsonDocument will have a RootElement
.
Let’s assign the root element to a variable;
Next, we initialize a new instance of StringBuilder
to build our CSV string;
We need the properties of each Job
so that we can write those properties as a row in the CSV file. But before we do that, we need to get the column headers. For this, we will iterate through the first object’s properties and add the the JsonProperty.Name
value to our builder object.
Next, we can print builder object to the console to check our headers.
Alright, that looks good! Now we can add the rows to our builder object.
We used the EnumerateObject
method to iterate through the JsonProperty
object and add JsonProperty.Value
our builder object. Finally, we write the builder string to a file.
After opening jobs.csv
, you will notice that we have a formatting issues.
Some of the description text in the first row was added to a new column. The program I loaded the CSV file into reads the comma in any text as the start of a new column. We could write a utility method to escape the commas before appending a new value to the builder object. A better way is to use the tools that Microsoft.Text.Json
provides to handle and implement any custom logic to convert JSON values.
I will create a custom converter to handle escaping the commas.
In the preceding code, I created a custom converted called JsonConverterEscapeCommas
which is derived from JsonConverter<string?>
and overrides the Read
and Write
methods. We only need to modify the Write
method since this post is focused on serialization.
In the Write
method body, we do a check to see if the value is not null and contains a comma. If it does, we surround the value with double quotes to adhere to the RFC 480 Standard.
Next, we have to implement our custom converter. There are a couple ways to do this. We can add the custom converter to the JsonSerializerOptions
like so:
However, this would not be ideal for our use case because every string value would be passed through the custom converter. We only want to apply this convert to the Description
property of the Job
class. We can do this by simply decorating the property on the model with the JsonConverterAttribute
.
Let’s test out the custom converter to see if the formatting issues in the CSV are resolved. After running the application, a new CSV file is created, and it looks great! 🎉
The System.Text.Json
namespace, included in the .NET base class library, is very useful, and this blog post barely scratches the surface of its capabilities. The demo provided in this post highlights how you can easily serialize objects and even control how the JsonSerializer
writes values, opening up many possibilities when customizing how values are converted to JSON.