Change the name "Key" and "Value" when using JSON.NET to serialize a complex dictionary

I am having a weird problem with serializing data using json.net. Basically, I am trying to rename the names "Key" and "Value" in the outgoing json to be something more descriptive. In particular, I want the "key" associated with IRequest to be called "Request", and the value of IQuoteTimeSeries to be "DataSeries"

Please note: this will not deserialize. It is used only when analyzing data on a web page.

The data repository object that I am serializing is the Dictionary<IRequest, IQuoteTimeSeries> . IRequest represents a specific data request, and IQuoteTimeSeries is an object containing the returned data as SortedDictionary<DateTime, IQuote> . This is a series of data sorted by timestamp. In this example, I have only one item in TimeSeries for brevity, but in most cases there will be many items.

Everything needs to be organized together, serialized and submitted to use JavaScript.

Here is the basic code for these objects;

 [JsonArray] public class QuoteRepository : Dictionary<IRequest, IQuoteTimeSeries>, IQuoteRepository { public QuoteRepository() { } public void AddRequest(IRequest request) { if (!this.ContainsKey(request)) { IQuoteTimeSeries tSeries = new QuoteTimeSeries(request); this.Add(request, tSeries); } } public void AddQuote(IRequest request, IQuote quote) { if (!this.ContainsKey(request)) { QuoteTimeSeries tSeries = new QuoteTimeSeries(request); this.Add(request, tSeries); } this[request].AddQuote(quote); } IEnumerator<IQuoteTimeSeries> Enumerable<IQuoteTimeSeries>.GetEnumerator() { return this.Values.GetEnumerator(); } } 

The time series of quotes are as follows:

 [JsonArray] public class QuoteTimeSeries: SortedDictionary<DateTime, IQuote>, IQuoteTimeSeries { public QuoteTimeSeries(IRequest request) { Request = request; } public IRequest Request { get; } public void AddQuote(IQuote quote) { this[quote.QuoteTimeStamp] = quote; } public void MergeQuotes(IQuoteTimeSeries quotes) { foreach (IQuote item in quotes) { this[item.QuoteTimeStamp] = item; } } IEnumerator<IQuote> IEnumerable<IQuote>.GetEnumerator() { return this.Values.GetEnumerator(); } } 

The code used for serialization is pretty simple:

 IQuoteRepository quotes = await requests.GetDataAsync(); JsonSerializerSettings settings = new JsonSerializerSettings { ContractResolver = QuoteRepositoryContractResolver.Instance, NullValueHandling = NullValueHandling.Ignore }; return Json<IQuoteRepository>(quotes, settings); 

I added a contract handler to override the property record. The property.PropertyName = code is deleted, and the property names are changed, but the output JSON is not changed.

 public class QuoteRepositoryContractResolver : DefaultContractResolver { public static readonly QuoteRepositoryContractResolver Instance = new QuoteRepositoryContractResolver(); protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); if (property.DeclaringType == typeof(KeyValuePair<IRequest, IQuoteTimeSeries>)) { if (property.PropertyName.Equals("Key", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "Request"; } else if (property.PropertyName.Equals("Value", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "Data"; } } else if (property.DeclaringType == typeof(KeyValuePair<DateTime, IQuote>)) { if (property.PropertyName.Equals("Key", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "TimeStamp"; } else if (property.PropertyName.Equals("Value", StringComparison.OrdinalIgnoreCase)) { property.PropertyName = "Quote"; } } return property; } } 

JSON output is odd. The Key and Value elements have not completely changed, although I changed their names in the code.

 [ { "Key": { "QuoteDate": "2016-05-12T00:00:00-04:00", "QuoteType": "Index", "Symbol": "SPY", "UseCache": true }, "Value": [ { "Key": "2016-05-11T16:00:01-04:00", "Value": { "Description": "SPDR S&amp;P 500", "High": 208.54, "Low": 206.50, "Change": -1.95, "ChangePer": -0.94, "Price": 206.50, "QuoteTimeStamp": "2016-05-11T16:00:01-04:00", "Symbol": "SPY" } } ] }, { "Key": { "QuoteDate": "2016-05-12T00:00:00-04:00", "QuoteType": "Stock", "Symbol": "GOOG", "UseCache": true }, "Value": [ { "Key": "2016-05-11T16:00:00-04:00", "Value": { "Description": "Alphabet Inc.", "High": 724.48, "Low": 712.80, "Change": -7.89, "ChangePer": -1.09, "Price": 715.29, "QuoteTimeStamp": "2016-05-11T16:00:00-04:00", "Symbol": "GOOG" } } ] } ] 

Does anyone know how to change the "Key" and "Value" elements correctly?

+5
source share
2 answers

One way to solve this problem is to use a custom JsonConverter for your dictionary-based classes. The code is actually quite simple.

 public class CustomDictionaryConverter<K, V> : JsonConverter { private string KeyPropertyName { get; set; } private string ValuePropertyName { get; set; } public CustomDictionaryConverter(string keyPropertyName, string valuePropertyName) { KeyPropertyName = keyPropertyName; ValuePropertyName = valuePropertyName; } public override bool CanConvert(Type objectType) { return typeof(IDictionary<K, V>).IsAssignableFrom(objectType); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { IDictionary<K, V> dict = (IDictionary<K, V>)value; JArray array = new JArray(); foreach (var kvp in dict) { JObject obj = new JObject(); obj.Add(KeyPropertyName, JToken.FromObject(kvp.Key, serializer)); obj.Add(ValuePropertyName, JToken.FromObject(kvp.Value, serializer)); array.Add(obj); } array.WriteTo(writer); } public override bool CanRead { get { return false; } } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } } 

When it's time to serialize, add the transformers to the JsonSerializerSettings as follows:

 JsonSerializerSettings settings = new JsonSerializerSettings { Converters = new List<JsonConverter> { new CustomDictionaryConverter<IRequest, IQuoteTimeSeries>("Request", "Data"), new CustomDictionaryConverter<DateTime, IQuote>("TimeStamp", "Quote") }, Formatting = Formatting.Indented }; string json = JsonConvert.SerializeObject(repo, settings); 

Fiddle: https://dotnetfiddle.net/roHEtx

+4
source

Converting a dictionary from Dictionary<IRequest, IQuoteTimeSeries> to List<KeyValuePair<IRequest, IQuoteTimeSeries>> should also work.

0
source

All Articles