I’ve used Newtonsoft’s Json.NET since as far back as I can remember. It is a fantastic package for working with objects and JSON. When .NET’s own Text.Json namespace was release in 2019, I -like many- largely ignored it. There just wasn’t a need to switch to the new package and many of our other packages had a dependency for Json.NET anyway.
In 2021, I was upgrading an old project to .NET 5 and felt that the time was right for the move to Text.Json.
I have no doubt that there are countless articles around, comparing the performance between the two packages. My goal was just to reduce the number of third-party package dependencies.
Is the serialised JSON produced the same? Is the code very different?
This is what I found.
Is the code very different?
In short, no.
Employee employee1 = new Employee();
string json;
// newtonsoft
json = Newtonsoft.Json.JsonConvert.SerializeObject(employee1);
employee1 = Newtonsoft.Json.JsonConvert.DeserializeObject<Employee>(json);
// text-json
json = System.Text.Json.JsonSerializer.Serialize(employee1);
employee1 = System.Text.Json.JsonSerializer.Deserialize<Employee>(json);
But also, yes, more about this later.
Is the JSON the same?
Yes, sort of but actually, no.
I serialised the same object with a number of common-typed properties. The results were identical with both packages.
public class Employee
{
public Guid ID { get; set; } = Guid.NewGuid();
public string Name { get; set; } = "Anders Anderson";
public DateTime DateOfBirth { get; set; } = new DateTime(2000, 1, 1);
public int TelExt { get; set; } = 101;
public KeyValuePair<int, int> Seat1 { get; set; } = new KeyValuePair<int, int>(0, 0);
public Tuple<int, int> Seat2 { get; set; } = new Tuple<int, int>(0, 0);
}
//{ID:e2064746-8dbf-4e1f-bba6-cb38c41fbdd7,Name:Anders Anderson,DateOfBirth:2000-01-01T00:00:00,TelExt:101,Seat1:{Key:0,Value:0},Seat2:{Item1:0,Item2:0}}
When I tested with some types from the System.Drawing namespace, the results were unfortunately different.
public class Employee
{
public Point Office { get; set; } = new Point(0, 0);
public Size Workspace { get; set; } = new Size(2, 1);
}
// newtonsoft
//{"Office":"0, 0","Workspace":"2, 1"}
// text-json
//{"Office":{"IsEmpty":true,"X":0,"Y":0},"Workspace":{"IsEmpty":false,"Width":2,"Height":1}}
Using user-defined converters
To retain backwards-compatibility for JSON created by Json.NET, we can define converters to customise how objects are serialised (and deserialised).
public class JsonPointConverter : JsonConverter
{
public override Point Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Point result = new Point(0, 0);
if (reader.TokenType == JsonTokenType.String)
{
string[] array = reader.GetString()!.Split(',');
if (array.Length != 2)
{
return result;
}
int result2 = 0;
int result3 = 0;
if (!int.TryParse(array[0].Trim(), out result2))
{
result2 = 0;
}
if (!int.TryParse(array[1].Trim(), out result3))
{
result3 = 0;
}
return new Point(result2, result3);
}
return result;
}
public override void Write(Utf8JsonWriter writer, Point value, JsonSerializerOptions options)
{
writer.WriteStringValue($"{value.X.ToString()}, {value.Y.ToString()}");
}
}
public class JsonSizeConverter : JsonConverter
{
public override Size Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Size result = new Size(0, 0);
if (reader.TokenType == JsonTokenType.String)
{
string[] array = reader.GetString()!.Split(',');
if (array.Length != 2)
{
return result;
}
int result2 = 0;
int result3 = 0;
if (!int.TryParse(array[0].Trim(), out result2))
{
result2 = 0;
}
if (!int.TryParse(array[1].Trim(), out result3))
{
result3 = 0;
}
return new Size(result2, result3);
}
return result;
}
public override void Write(Utf8JsonWriter writer, Size value, JsonSerializerOptions options)
{
writer.WriteStringValue($"{value.Width.ToString()}, {value.Height.ToString()}");
}
}
The converters above simplify the Point and Size types in a way that I thought would be obvious.
The implementation would be like this.
Employee employee1 = new Employee();
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonPointConverter());
options.Converters.Add(new JsonSizeConverter());
string json = System.Text.Json.JsonSerializer.Serialize(employee1, options);
employee1 = System.Text.Json.JsonSerializer.Deserialize<Employee>(json, options);
And one last thing.
I encountered an unexpected problem when I had special characters in the JSON. Json.NET would escape the characters using the traditional slash (“\”) but Text.Json would encode the characters as unicode. This shouldn’t be a problem except that not a single JSON editor I found works that way. I end up with the unicode characters being stripped out.
public class Employee
{
public string Name { get; set; } = "Anders \"Andy\" Anderson";
}
// newtonsoft
//{"Name":"Anders \"Andy\" Anderson"}
// text-json
//{"Name":"Anders \u0022Andy\u0022 Anderson"}
Lucky, this is an easy fix in the options.
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonPointConverter());
options.Converters.Add(new JsonSizeConverter());
options.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
string json = System.Text.Json.JsonSerializer.Serialize(employee1, options);
employee1 = System.Text.Json.JsonSerializer.Deserialize<Employee>(json, options);
Well, that’s that.
I hope someone finds this useful.
Posted on Sat 12th Mar 2022
Modified on Sat 5th Aug 2023