There is no built-in way to configure property names from injected IConfiguration
values at runtime. If you need this, you will have to implemented it yourself using contract customization in Json.NET (any version) or System.Text.Json (in .NET 7 and later). Since you tagged your question json.net but used the attribute [JsonPropertyName]
from System.Text.Json I'll give answers for both.
Whichever serializer you use, first create a custom attribute for the configuration key you want to use for a given property:
[System.AttributeUsage(System.AttributeTargets.Property | System.AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class JsonConfiguredPropertyNameAttribute : System.Attribute
{
public string Key { get; }
public JsonConfiguredPropertyNameAttribute(string key) => this.Key = key;
}
Next, apply it to your class as follows:
public class Project {
public string ProjectId { get; set; }
[JsonConfiguredPropertyName("fieldConfig:projectNameField")]
public string ProjectName { get; set; }
[JsonConfiguredPropertyName("fieldConfig:projectDescriptionField")]
public string ProjectDescription { get; set; }
[JsonConfiguredPropertyName("fieldConfig:projectCreatorField")]
public string ProjectCreator { get; set; }
}
Note that, as explained in Configuration keys and values, hierarchical keys should be referred to using a colon separator :
:
"fieldConfig:projectNameField"
If you use Json.NET, create the following custom contract resolver:
public class PropertyNamesFromConfigurationContractResolver : DefaultContractResolver
{
readonly IConfiguration config;
public PropertyNamesFromConfigurationContractResolver(IConfiguration config) => this.config = config ?? throw new ArgumentNullException(nameof(config));
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (member.GetCustomAttribute<JsonConfiguredPropertyNameAttribute>()?.Key is {} key)
property.PropertyName = config[key] ?? property.PropertyName;
return property;
}
}
Then, configure your JsonSerializerSettings
using some injected IConfiguration
as follows:
var settings = new JsonSerializerSettings();
settings.ContractResolver = new PropertyNamesFromConfigurationContractResolver(config)
{
// Add other properties as required, e.g.
NamingStrategy = new CamelCaseNamingStrategy(),
};
Note that Json.NET's contract resolver caches contracts, so if your configuration changes you will have to construct a new instance of PropertyNamesFromConfigurationContractResolver
.
Demo fiddle #1 here.
If you use System.Text.Json, then in .NET 7 and later you can use a typeInfo modifier to customize your type's contract in a similar manner.
First, create the modifier:
public class JsonExtensions
{
public static Action<JsonTypeInfo> InitializePropertyNamesFromConfiguration(IConfiguration config) =>
typeInfo =>
{
if (typeInfo.Kind == JsonTypeInfoKind.Object)
foreach (var property in typeInfo.Properties)
if ((property.AttributeProvider?
.GetCustomAttributes(typeof(JsonConfiguredPropertyNameAttribute), true)
.Cast<JsonConfiguredPropertyNameAttribute>()
.SingleOrDefault()?.Key) is {} key)
property.Name = config[key] ?? property.Name;
};
}
Then when setting up your JsonSerializerOptions
apply the modifer as follows, once again making use of some injected IConfiguration
:
var options = new JsonSerializerOptions();
options.TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { JsonExtensions.InitializePropertyNamesFromConfiguration(config) },
};
// Add other options as required, e.g.
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
Notes:
There is no way to customize System.Text.Json contracts in .NET versions earlier than .NET 7.
System.Text.Json is case sensitive (unlike Json.NET) so the field names will have to match exactly unless you set JsonSerializerOptions.PropertyNameCaseInsensitive = true
.
I believe that DefaultJsonTypeInfoResolver
also caches contracts, so if your configuration changes you may need to replace it with a new instance.
Demo fiddle #2 here.