using System.Collections.Generic;
using System.Text.Json.Serialization;
var jsonSettings = new JsonSerializerOptions()
new CollectionJsonConverter(),
var data = new PreferredTermData
var serialize = JsonSerializer.Serialize(data, jsonSettings);
var preferredTermData = JsonSerializer.Deserialize<PreferredTermData>(serialize, jsonSettings);
public class CollectionJsonConverter : JsonConverterFactory
public override bool CanConvert(Type typeToConvert)
return !IsExcludedType(typeToConvert) &&
typeToConvert.IsGenericType &&
IsAssignableToGenericType(typeToConvert, typeof(IEnumerable<>));
private static bool IsExcludedType(Type type)
return type.IsPrimitive ||
private static bool IsAssignableToGenericType(Type givenType, Type genericType)
var interfaceTypes = givenType.GetInterfaces()
.Where(x => x.IsGenericType)
.Select(x => x.GetGenericTypeDefinition());
return interfaceTypes.Contains(genericType);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
var elementType = typeToConvert.GetGenericArguments()[0];
var converterType = typeof(ReadOnlyCollectionJsonConverter<>).MakeGenericType(elementType);
var instance = Activator.CreateInstance(converterType);
throw new JsonException();
return (JsonConverter) instance;
public sealed class ReadOnlyCollectionJsonConverter<TElement> : JsonConverter<IReadOnlyCollection<TElement>>
public override IReadOnlyCollection<TElement> Read(
ref Utf8JsonReader reader,
JsonSerializerOptions options)
Console.WriteLine("ReadOnlyCollectionJsonConverter.Read");
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException();
var list = new List<TElement>();
if (reader.TokenType == JsonTokenType.EndArray)
var elementValue = JsonSerializer.Deserialize<TElement>(ref reader, options);
if (elementValue is not null)
public override void Write(
IReadOnlyCollection<TElement> value,
JsonSerializerOptions options)
writer.WriteStartArray();
foreach (var element in value)
JsonSerializer.Serialize(writer, element, options);
public class TermDataConverter : JsonConverter<TermData>
public override TermData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
Console.WriteLine("TermDataConverter.Read");
if (reader.TokenType is JsonTokenType.String)
var str = reader.GetString();
return str is not null ? new TermData {Term = str} : null;
var jsonSerializerOptions = CopyOptsWithoutSelf(options);
return JsonSerializer.Deserialize<TermData>(ref reader, jsonSerializerOptions);
public override void Write(Utf8JsonWriter writer, TermData value, JsonSerializerOptions options)
var jsonSerializerOptions = CopyOptsWithoutSelf(options);
JsonSerializer.Serialize(writer, value, jsonSerializerOptions);
private static JsonSerializerOptions CopyOptsWithoutSelf(JsonSerializerOptions options)
var jsonSerializerOptions = new JsonSerializerOptions(options);
var jsonConverter = jsonSerializerOptions.Converters
.FirstOrDefault(converter => converter.GetType() == typeof(TermDataConverter));
if (jsonConverter is not null)
jsonSerializerOptions.Converters.Remove(jsonConverter);
return jsonSerializerOptions;
[JsonPropertyName("trash_id")]
public string TrashId { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public string Term { get; init; } = string.Empty;
public record PreferredTermData
public int Score { get; init; }
public IReadOnlyCollection<TermData> Terms { get; init; } = Array.Empty<TermData>();
public record ReleaseProfileData
[JsonPropertyName("trash_id")]
public string TrashId { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public bool IncludePreferredWhenRenaming { get; init; }
public IReadOnlyCollection<TermData> Required { get; init; } = Array.Empty<TermData>();
public IReadOnlyCollection<TermData> Ignored { get; init; } = Array.Empty<TermData>();
public IReadOnlyCollection<PreferredTermData> Preferred { get; init; } = Array.Empty<PreferredTermData>();