using System.Text.Json.Serialization;
using System.Collections.Generic;
public static void Main()
var oldPosition = new Position(50, 55);
var json = JsonSerializer.Serialize<Position>(oldPosition);
Console.WriteLine("Position->JSON: " + json);
var position = JsonSerializer.Deserialize<Position>(json);
json = JsonSerializer.Serialize<Position>(position);
Console.WriteLine("JSON->Position->JSON: " + json);
[StructJsonConverter(typeof(Position))]
public readonly struct Position
public Position(int x, int y)
[AttributeUsage(AttributeTargets.Struct)]
public class StructJsonConverterAttribute : JsonConverterAttribute
public StructJsonConverterAttribute(Type targetType)
: base(typeof(StructJsonConverter<>).MakeGenericType(targetType))
internal class StructJsonConverter<T> : JsonConverter<T> where T : struct
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
using var jsonDoc = JsonDocument.ParseValue(ref reader);
var json = jsonDoc.RootElement.GetRawText();
if (string.IsNullOrEmpty(json))
var fieldValues = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json).Values;
var valueCount = fieldValues.Count;
var constructors = typeToConvert.GetConstructors();
foreach (var constructor in constructors)
var parameters = constructor.GetParameters();
if (parameters.Length != valueCount)
var values = new object[valueCount];
foreach (var element in fieldValues)
var valueType = parameters[index].ParameterType;
values[index] = element.Deserialize(valueType);
return (T)constructor.Invoke(values);
public override void Write(Utf8JsonWriter writer, T generic, JsonSerializerOptions options)
writer.WriteStartObject();
var fields = typeof(T).GetFields();
foreach (var field in fields)
var type = field.FieldType;
var value = field.GetValue(generic);
var element = JsonSerializer.SerializeToElement(value, type);
writer.WritePropertyName(field.Name);
writer.WriteRawValue(element.GetRawText());