using NodaTime.Serialization.JsonNet;
using NodaTime.Serialization.SystemTextJson;
using System.Collections.Generic;
static JsonSerializerSettings serializerSettings = new JsonSerializerSettings(){
Converters = new List<JsonConverter>{
new EnergyJsonConverter()
}.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
static JsonSerializerOptions serializerOptions = new JsonSerializerOptions(){
PropertyNameCaseInsensitive = true,
new EnergySystemTextJsonConverter()
}.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
public static void Main()
TestSerialization(new DeviceStateChangedEvent<Guid>("device1", Guid.NewGuid()));
TestSerialization(new DeviceStateChangedEvent<Duration>("device1", Duration.FromHours(1)));
TestSerialization(new DeviceStateChangedEvent<RGBWColor2>("device1", new RGBWColor2(1, 2, 3)));
TestSerialization(new DeviceStateChangedEvent<Energy>("device1", new Energy(2)));
public static void TestSerialization<T>(T obj)
var s2 = Newtonsoft.Json.JsonConvert.SerializeObject(obj, serializerSettings);
Console.WriteLine("Newtonsoft.Json: " + s2);
var newObj = System.Text.Json.JsonSerializer.Deserialize<T>(s2, serializerOptions);
Console.WriteLine("System text json: " + newObj);
Console.WriteLine("Serialized with text json: " + System.Text.Json.JsonSerializer.Serialize<T>(newObj, serializerOptions));
public record DeviceStateChangedEvent<TState>(string DeviceId, TState? State) where TState : struct;
public record struct RGBWColor2(byte R, byte G, byte B, byte W = 0)
public static implicit operator Color(RGBWColor2 color)
return Color.FromArgb(color.W, color.W, color.W);
return Color.FromArgb(color.R, color.G, color.B);
public static implicit operator RGBWColor2(Color color)
return new RGBWColor2(color.R, color.G, color.B, 0);
public RGBWColor2 ExtractWhite()
return new RGBWColor2(0, 0, 0, R);
public readonly struct RGBWColor
public readonly byte R, G, B, W;
public RGBWColor(byte r, byte g, byte b, byte w = 0)
public static implicit operator Color(RGBWColor color)
return Color.FromArgb(color.W, color.W, color.W);
return Color.FromArgb(color.R, color.G, color.B);
public static implicit operator RGBWColor(Color color)
return new RGBWColor(color.R, color.G, color.B, 0);
public RGBWColor ExtractWhite()
return new RGBWColor(0, 0, 0, R);
public override string ToString() => (R, G, B, W).ToString();
public record struct Energy(double WattHour)
public static readonly Energy Zero = new Energy(0);
public static Energy FromWattHour(double wattHour) => new Energy(wattHour);
public static Energy? FromWattHour(double? wattHour) => wattHour == null ? null : new Energy(wattHour.Value);
public static Energy FromKiloWattHour(double kWh) => new Energy(kWh * 1000);
public static Energy operator -(Energy e1, Energy e2) => new Energy(e1.WattHour - e2.WattHour);
public static Energy operator +(Energy e1, Energy e2) => new Energy(e1.WattHour + e2.WattHour);
class EnergyJsonConverter : Newtonsoft.Json.JsonConverter<Energy?>
public override void WriteJson(JsonWriter writer, Energy? value, Newtonsoft.Json.JsonSerializer serializer)
serializer.Serialize(writer, value.Value.WattHour);
public override Energy? ReadJson(JsonReader reader, Type objectType, Energy? existingValue, bool hasExistingValue,
Newtonsoft.Json.JsonSerializer serializer)
return Energy.FromWattHour((double?)reader.Value);
class EnergySystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<Energy?>
public override Energy? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
return Energy.FromWattHour(reader.TokenType == JsonTokenType.Null ? null : reader.GetDouble());
public override void Write(Utf8JsonWriter writer, Energy? value, JsonSerializerOptions options)
writer.WriteNumberValue(value.Value.WattHour);