Skip to content

Instantly share code, notes, and snippets.

@Jozkee
Created January 5, 2021 02:25
Show Gist options
  • Save Jozkee/88ac585c1912f6a3c6816254cecc54b5 to your computer and use it in GitHub Desktop.
Save Jozkee/88ac585c1912f6a3c6816254cecc54b5 to your computer and use it in GitHub Desktop.
preserving references + custom converters
// Assuming that you wrote a custom converter for `Company` and you don't want to manually serialize the `Supervisor`, which is an `Employee`.
// you want to delegate that to the Serializer and you also want to preserve the references that you have already saved.
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ConsoleApp4
{
class Program
{
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public List<Employee> DirectReports { get; set; }
public Company Company { get; set; }
}
public class Company
{
public string Name { get; set; }
public Employee Supervisor { get; set; }
}
static void Main(string[] args)
{
Employee tyler = new()
{
Name = "Tyler Stein"
};
Employee adrian = new()
{
Name = "Adrian King"
};
Company contoso = new()
{
Name = "Contoso",
Supervisor = tyler
};
tyler.DirectReports = new List<Employee> { adrian };
adrian.Manager = tyler;
tyler.Company = contoso;
adrian.Company = contoso;
var options = new JsonSerializerOptions();
options.Converters.Add(new CompanyConverter());
var myReferenceHandler = new MyReferenceHandler();
options.ReferenceHandler = myReferenceHandler;
options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.WriteIndented = true;
string str = JsonSerializer.Serialize(tyler, options);
// Always reset after you are done serializing in order to avoid out of bounds memory growth in the resolver.
myReferenceHandler.Reset();
Console.WriteLine(str);
// Output:
//{
// "$id": "1",
// "Name": "Tyler Stein",
// "DirectReports": {
// "$id": "2",
// "$values": [
// {
// "$id": "3",
// "Name": "Adrian King",
// "Manager": {
// "$ref": "1"
// },
// "Company": {
// "Name": "Contoso",
// "Supervisor": {
// "$ref": "1"
// }
// }
// }
// ]
// },
// "Company": {
// "Name": "Contoso",
// "Supervisor": {
// "$ref": "1"
// }
// }
//}
}
class CompanyConverter : JsonConverter<Company>
{
public override Company Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, Company value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("Name", value.Name);
writer.WritePropertyName("Supervisor");
JsonSerializer.Serialize(writer, value.Supervisor, options);
writer.WriteEndObject();
}
}
class MyReferenceHandler : ReferenceHandler
{
private ReferenceResolver _rootedResolver;
public MyReferenceHandler() => Reset();
public override ReferenceResolver CreateResolver() => _rootedResolver;
public void Reset() => _rootedResolver = new MyReferenceResolver();
class MyReferenceResolver : ReferenceResolver
{
private uint _referenceCount;
private readonly Dictionary<string, object> _referenceIdToObjectMap = new Dictionary<string, object>();
private readonly Dictionary<object, string> _objectToReferenceIdMap = new Dictionary<object, string>(ReferenceEqualityComparer.Instance);
public override void AddReference(string referenceId, object value)
{
if (!_referenceIdToObjectMap.TryAdd(referenceId, value))
{
throw new JsonException();
}
}
public override string GetReference(object value, out bool alreadyExists)
{
if (_objectToReferenceIdMap.TryGetValue(value, out string referenceId))
{
alreadyExists = true;
}
else
{
_referenceCount++;
referenceId = _referenceCount.ToString();
_objectToReferenceIdMap.Add(value, referenceId);
alreadyExists = false;
}
return referenceId;
}
public override object ResolveReference(string referenceId)
{
if (!_referenceIdToObjectMap.TryGetValue(referenceId, out object value))
{
throw new JsonException();
}
return value;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment