In .NET 7, is there a way to serialize a const?
internal sealed class CreateResourceCommand : BaseCommand
{
public const string CommandName = "CreateResourceCommand";
}
I have many Commands derived from a base who are sent to a message queue. The remote consumer will deserialize the payload and not get the CommandName.
It is straightforward with Newtonsoft.Json
but I can't make it work with System.Text.Json.Serialization
.
I tried :
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
JsonConverter
but the const won't appear in the valueAs mentioned in comments by Serge, serializing const
values is a bit of an odd thing to do, and is apparently not implemented out of the box even if you apply [JsonInclude]
. Thus you're going to need to create a typeInfo modifier to serialize them.
First define the following modifiers to force serialization of const
values when some attribute TOptInAttribute
is applied:
public static partial class JsonExtensions
{
// Include opted-in constants for the specified type.
public static Action<JsonTypeInfo> AddOptInConstMembers<TOptInAttribute>(Type type) where TOptInAttribute : System.Attribute =>
typeInfo =>
{
if (typeInfo.Type == type)
AddOptInConstMembers<TOptInAttribute>(typeInfo);
};
// Include opted-in constants for all types.
public static void AddOptInConstMembers<TOptInAttribute>(JsonTypeInfo typeInfo) where TOptInAttribute : System.Attribute
{
if (typeInfo.Kind != JsonTypeInfoKind.Object)
return;
foreach (var field in typeInfo.Type.GetConstants().Where(f => Attribute.IsDefined(f, typeof(TOptInAttribute))))
{
var name = field.GetCustomAttribute<JsonPropertyNameAttribute>()?.Name ?? typeInfo.Options.PropertyNamingPolicy?.ConvertName(field.Name) ?? field.Name;
var value = field.GetValue(null); // field.GetValue(null); returns enums as enums rathen raw integers.
var propertyInfo = typeInfo.CreateJsonPropertyInfo(value?.GetType() ?? field.FieldType, name);
propertyInfo.Get = (o) => value;
propertyInfo.CustomConverter = field.GetCustomAttribute<JsonConverterAttribute>()?.ConverterType is {} converterType
? (JsonConverter?)Activator.CreateInstance(converterType)
: null;
typeInfo.Properties.Add(propertyInfo);
}
}
static IEnumerable<FieldInfo> GetConstants(this Type type) =>
// From the answer https://stackoverflow.com/a/10261848
// By https://stackoverflow.com/users/601179/gdoron
// To https://stackoverflow.com/questions/10261824/how-can-i-get-all-constants-of-a-type-by-reflection
type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(fi => fi.IsLiteral && !fi.IsInitOnly);
}
Then apply your chosen TOptInAttribute
attribute to the const
fields to be serialized. It could be your own custom attribute or [JsonInclude]
as per your preference:
internal sealed class CreateResourceCommand : BaseCommand
{
[JsonInclude]
public const string CommandName = "CreateResourceCommand";
}
Finally, to serialize const
fields with that attribute applied (here [JsonInclude]
), use the following options:
var command = new CreateResourceCommand();
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { JsonExtensions.AddOptInConstMembers<JsonIncludeAttribute> },
},
};
var json = JsonSerializer.Serialize(command, options);
Console.WriteLine(json); // Prints {"CommandName":"CreateResourceCommand"}
Assert.AreEqual("""{"CommandName":"CreateResourceCommand"}""", json);
Of course the const
values are read-only and will not be deserialized.
Demo fiddle here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With