I have a need to format the output json of a decimal to a currency, with the culture specified my the object I am serializing, the object could be nested so I cannot preset the option in the serializer. The current way I am doing this is by using extra string properties that format the output.
[JsonIgnore]
public decimal Cost {get;set;}
[JsonIgnore]
public CultureInfo Culture {get;set;}
public string AsCurrency(decimal value) {
return string.Format(this.Culture, "{0:c}", value);
}
[JsonProperty("FormattedCost")]
public string FormatedCost {
get { return this.AsCurrency(this.Cost); }
}
I have alot of properties to deal with, I'm not bothered about Deserializing, the JsonObject is used by a different language to populated a PDF and so I want the string values.
Ideally I'd like a JsonConverter
so I can just do
[JsonProperty("FormattedCost")]
[JsonConverter(typeof(MyCurrencyConverter))]
public decimal Cost {get;set;}
The issue I have is how to access the Culture property of the containing object in the converter.
public class MyCurrencyConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var culture = // How do I get the Culture from the parent object?
writer.WriteValue(string.format(culture, "{0:c}", (decimal)value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(decimal) == objectType;
}
}
As Requested sample JSON.
for an array of Contract
classes that each have a Cost and an Culture.
[{ FormattedCost : "£5000.00"}, { FormattedCost : "$8000.00"}, { FormattedCost : "€599.00"}]
The actual objects are a lot more complicated, multiple fields with nested Assets that would have their own figures. Additionally not all decimals would be currencies.
I don't really want to have to write a custom serializer for the Contract itself as I would then have to modify it each time the properties change.
The ideal solution is being able to tag certain decimal properties with the converter attribute so it can handle it.
The other way I was thinking of going was to make a custom class for the decimal properties with an implicit conversion from decimal, however that gets more complicated as some properties are calculated properties based on previous results.
WORKAROUND
I have a work-around for my use case, but it uses reflection to obtain a private variable in the serializer.
var binding = BindingFlags.NonPublic | BindingFlags.Instance;
var writer = serializer.GetType()
.GetMethod("GetInternalSerializer", binding)
?.Invoke(serializer, null);
var parent = writer?.GetType()
.GetField("_serializeStack", binding)
?.GetValue(writer) is List<object> stack
&& stack.Count > 1 ? stack[stack.Count - 2] as MyType: null;
In my tested use cases this gives me the parent object, but it's not using the public API.
See Question&Answers more detail:
os