I also missed this feature from System.Text.Json.Serialization, and used to use a custom JsonConverter for every formatting case, but I really did not like. My best workorund to solve this a cleaner way - at least for my taste - uses a custom JsonConverterAttribute.
I use this in .NET6 apps, but according to the docs, it works with the Core 3.1, too.
So the example:
Create a Converter that requires a constructor parameter (based on the question that is done already). In my case it is the format string.
public class DoubleConverter : JsonConverter<double>
{
private readonly string _format;
public DoubleConverter(string format) => _format = format;
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// Not needed for the example.
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(_format));
}
}
Then create a custom JsonAttribute. This part makes the usage easy, because it accepts the needed constructor parameter, and creates the cutom JsonConverter using that parameter.
[AttributeUsage(AttributeTargets.Property)]
public class DoubleSerializationStringFormatAttribute : JsonConverterAttribute
{
private readonly string _format;
public DoubleSerializationStringFormatAttribute(string format) => _format = format;
public override JsonConverter CreateConverter(Type typeToConvert)
{
if (typeToConvert != typeof(double))
{
throw new ArgumentException(
$"This converter only works with double, and it was provided {typeToConvert.Name}.");
}
return new DoubleConverter(_format);
}
}
Now the attribute can be used on any property:
public class DataClass
{
[DoubleSerializationStringFormat("N2")]
public double Prop1 { get; set; }
[DoubleSerializationStringFormat("N5")]
public double Prop2 { get; set; }
}
Finally I can serialize the DataClass instance:
var data = new DataClass() {Prop1 = 10.5678, Prop2 = 3.14159267};
var serialized = JsonSerializer.Serialize(data);
Console.Write(serialized);
And I get the numbers serialized according to the specified format:
{
"Prop1":"10.57",
"Prop2":"3.14159"
}