Skip to content

Instantly share code, notes, and snippets.

@chrisbewz
Created March 17, 2025 12:09
Show Gist options
  • Select an option

  • Save chrisbewz/ca49427d113deb84ea8e881a32ee68e7 to your computer and use it in GitHub Desktop.

Select an option

Save chrisbewz/ca49427d113deb84ea8e881a32ee68e7 to your computer and use it in GitHub Desktop.
System.Text.Json UnitsNet Custom Quantity Converter Sample
public sealed class ElectricPotentialConverter : QuantityConverter<ElectricPotential, ElectricPotentialUnit>
{
protected override ElectricPotential FromValueAndUnit(double value, ElectricPotentialUnit unit)
{
return ElectricPotential.From(value, unit);
}
protected override ElectricPotentialUnit GetUnit(ElectricPotential quantity)
{
return quantity.Unit;
}
protected override double GetValue(ElectricPotential quantity)
{
return quantity.Value;
}
}
using System.Text.Json;
using System.Text.Json.Serialization;
using UnitsNet;
using UnitsNet.Units;
public abstract class QuantityConverter<TQuantity, TUnit> : JsonConverter<TQuantity>
where TQuantity : IQuantity
where TUnit : struct, Enum
{
protected abstract TQuantity FromValueAndUnit(double value, TUnit unit);
protected abstract TUnit GetUnit(TQuantity quantity);
protected abstract double GetValue(TQuantity quantity);
public override TQuantity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException($"Expected start of object for {typeof(TQuantity).Name}");
double value = 0;
TUnit unit = default;
bool valueSet = false;
bool unitSet = false;
reader.Read();
string valuePropertyName = options.GetPropertyName(nameof(IQuantity.Value));
string unitPropertyName = options.GetPropertyName(nameof(IQuantity.Unit));
while (reader.TokenType != JsonTokenType.EndObject)
{
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Expected property name");
string propertyName = reader.GetString();
reader.Read();
if (propertyName == valuePropertyName)
{
value = reader.GetDouble();
valueSet = true;
}
else if (propertyName == unitPropertyName)
{
string unitString = reader.GetString();
if(!Enum.TryParse<TUnit>(unitString, out var parsedUnit))
throw new JsonException($"Unknown unit: {unitString} for {typeof(TQuantity).Name}");
unit = parsedUnit;
unitSet = true;
}
else
reader.Skip();
reader.Read();
}
if (!valueSet || !unitSet)
throw new JsonException($"Required property missing for {typeof(TQuantity).Name}");
return FromValueAndUnit(value, unit);
}
public override void Write(Utf8JsonWriter writer, TQuantity value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WritePropertyName(options.GetPropertyName(nameof(IQuantity.Value)));
writer.WriteNumberValue(GetValue(value));
writer.WritePropertyName(options.GetPropertyName(nameof(IQuantity.Unit)));
writer.WriteStringValue(GetUnit(value).ToString());
writer.WriteEndObject();
}
}
internal static class SystemTextJsonSerializerExtensions
{
/// <summary>
/// Returns a formatted property name accordingly to provided <see cref="JsonSerializerOptions"/>
/// </summary>
public static string GetPropertyName(this JsonSerializerOptions options, string name)
{
return options.PropertyNamingPolicy == null
? name
: options.PropertyNamingPolicy.ConvertName(name);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment