Reputation: 1499
I have this class:
public class ValueInteger
{
[JsonIgnore]
public string ValueName { get; set; }
public int Value { get; set; }
[JsonProperty("timestamp")]
public UInt64 TimeStamp { get; set; }
}
Given an instance of:
var valueInt = new ValueInteger
{
ValueName = "mycounter",
Value = 7,
TimeStamp = 1010101010
}
It should serialize to:
{ mycounter: 7, timestamp = 1010101010 }
It would be cool if one could declare the Value property as
[JsonRedirect(titlePropertyName: nameof(ValueName))]
public int Value { get; set; }
I probably have to implement my own ContractResolver, and have studiet this post: https://stackoverflow.com/a/47872645/304820 but it depends on the IValueProvider, and AFAIK there is no INameProvider to use for renaming.
Usually renaming is done per class, not per instance.
Upvotes: 3
Views: 11127
Reputation: 4194
My approach to this would be to write my own Converter. The converter simply serializes in the same fashion as a normal converter, but whenever it comes across a special attribute on a property, it should rename that property in the output.
So, serializing a C# object would go like this:
I have made a simple implementation of this. The usage looks like this:
class Program
{
static void Main(string[] args)
{
var valueInt = new ValueInteger
{
ValueName = "mycounter",
Value = 7,
TimeStamp = 1010101010
};
var settings = new JsonSerializerSettings { Converters = new JsonConverter[] { new DynamicNameConverter() } };
var result = JsonConvert.SerializeObject(valueInt, settings);
Console.WriteLine(result);
Console.Read();
}
}
public class ValueInteger
{
[JsonIgnore]
public string ValueName { get; set; }
[JsonDynamicName(nameof(ValueName))]
public int Value { get; set; }
[JsonProperty("timestamp")]
public UInt64 TimeStamp { get; set; }
}
And the helper classes:
class DynamicNameConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
// Only use this converter for classes that contain properties with an JsonDynamicNameAttribute.
return objectType.IsClass && objectType.GetProperties().Any(prop => prop.CustomAttributes.Any(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute)));
}
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// We do not support deserialization.
throw new NotImplementedException();
}
public override bool CanWrite => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var token = JToken.FromObject(value);
if (token.Type != JTokenType.Object)
{
// We should never reach this point because CanConvert() only allows objects with JsonPropertyDynamicNameAttribute to pass through.
throw new Exception("Token to be serialized was unexpectedly not an object.");
}
JObject o = (JObject)token;
var propertiesWithDynamicNameAttribute = value.GetType().GetProperties().Where(
prop => prop.CustomAttributes.Any(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute))
);
foreach (var property in propertiesWithDynamicNameAttribute)
{
var dynamicAttributeData = property.CustomAttributes.FirstOrDefault(attr => attr.AttributeType == typeof(JsonDynamicNameAttribute));
// Determine what we should rename the property from and to.
var currentName = property.Name;
var propertyNameContainingNewName = (string)dynamicAttributeData.ConstructorArguments[0].Value;
var newName = (string)value.GetType().GetProperty(propertyNameContainingNewName).GetValue(value);
// Perform the renaming in the JSON object.
var currentJsonPropertyValue = o[currentName];
var newJsonProperty = new JProperty(newName, currentJsonPropertyValue);
currentJsonPropertyValue.Parent.Replace(newJsonProperty);
}
token.WriteTo(writer);
}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
class JsonDynamicNameAttribute : Attribute
{
public string ObjectPropertyName { get; }
public JsonDynamicNameAttribute(string objectPropertyName)
{
ObjectPropertyName = objectPropertyName;
}
}
Please be aware that a lot of error handling could be put into DynamicNameConverter
but I have left it out to make it easier to read and understand.
Upvotes: 5