JSON serialization using newtonsoft in C# - c#

I have the following model structure.
public class ReferenceData
{
public string Version { get; set; }
public List<DataItem> Data { get; set; }
}
public class DataItem
{
public Dictionary<string, string> Item { get; set; }
}
In the dictionary i'm adding the key value pair and serializing with KeyValuePairConverter setting.
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
Converters = new List<JsonConverter>() { new KeyValuePairConverter() }
};
var object = Newtonsoft.Json.JsonConvert.SerializeObject(
referenceData,
Formatting.None,
settings
);
And the output is,
{
"data":[
{
"item":{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP",
}
},
{
"item":{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP",
}
},
{
"item":{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP",
}
}
]
}
If we don't want item to be showed in the serialized string, what setting needs to be done in JsonSerializerSettings or is there any other way to do that.
Please note that i can not change the model structure as it is required.
output should be :
{
"data":[
{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP"
},
{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP"
},
{
"ShortDescription":"Lorem ipssumm",
"Title":"some text",
"PlanType":"ZEROP"
}
]
}

You do not need nested generic collections if you use Json.NET 5.0 release 5 or later version.
You can use JsonExtensionDataAttribute so that Item dictionary's keys and values will be serialized as a part of parent object.
public class ReferenceData
{
public string version { get; set; }
public List<DataItem> data { get; set; }
}
public class DataItem
{
[JsonExtensionData]
public IDictionary<string, object> item { get; set; }
}
// ...
var referenceData = new ReferenceData {
version = "1.0",
data = new List<DataItem> {
new DataItem {
item = new Dictionary<string, object> {
{"1", "2"},
{"3", "4"}
}
},
new DataItem {
item = new Dictionary<string, object> {
{"5", "8"},
{"6", "7"}
}
}
}
};
Console.WriteLine(JsonConvert.SerializeObject(referenceData));
Pay attention that you need Dictionary<string, object> instead of Dictionary<string, string>.
Here is the result I get:
{
"version": "1.0",
"data": [
{
"1": "2",
"3": "4"
},
{
"5": "8",
"6": "7"
}
]
}
Obviously, you can remove Version property to get the expected result.
Read more here:
http://james.newtonking.com/archive/2013/05/08/json-net-5-0-release-5-defaultsettings-and-extension-data

If you change like this result will be what you expected;
public class ReferenceData
{
public string Version { get; set; }
public List<Dictionary<string, string>> Data { get; set; }
}
possible other solution is;
ReferenceData r = new ReferenceData();
r.Data = new List<DataItem>();
r.Data.Add(new DataItem { Item = new Dictionary<string, string>() { { "1", "2" }, { "3", "4" } } });
var anon = new
{
data = r.Data.ToList().Select(x =>
{
dynamic data = new ExpandoObject();
IDictionary<string, object> dictionary = (IDictionary<string, object>)data;
foreach (var key in x.Item.Keys)
dictionary.Add(key, x.Item[key]);
return dictionary;
}
)
};
var result = JsonConvert.SerializeObject(anon);
result :
{
"data": [
{
"1": "2",
"3": "4"
}
]
}

If you can't change the C# can use you a View model and use an appropriate structure. It's probably simpler than changing JSON settings, easier to return to and more explicit:
public class ReferenceData
{
public string Version { get; set; }
public List<Dictionary<string, string>> Data { get; set; }
}
Should serialise as you require.

You could implement a custom behaviour as follows:
class Program {
static void Main(string[] args) {
var referenceData = new ReferenceData() {
Data = new List<DataItem>() {
new DataItem(){
Item = new Dictionary<string,string>() {
{"ShortDescription", "Lorem ipssumm"},
{"Title", "some text"},
{"PlanType", "ZEROP"},
}
},
new DataItem(){
Item = new Dictionary<string,string>() {
{"ShortDescription", "Lorem ipssumm"},
{"Title", "some text"},
{"PlanType", "ZEROP"},
}
},
new DataItem(){
Item = new Dictionary<string,string>() {
{"ShortDescription", "Lorem ipssumm"},
{"Title", "some text"},
{"PlanType", "ZEROP"},
}
}
}
};
var settings = new JsonSerializerSettings {
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
Converters = new List<JsonConverter>() { new KeyValuePairConverter(), new CustomJsonSerializableConverter() }
};
File.WriteAllText("hello.json", Newtonsoft.Json.JsonConvert.SerializeObject(
referenceData,
Formatting.Indented,
settings
));
}
}
public class ReferenceData {
public string Version { get; set; }
public List<DataItem> Data { get; set; }
}
[CustomJsonSerializable]
public class DataItem {
public Dictionary<string, string> Item { get; set; }
public static void WriteJson(JsonWriter writer, DataItem value, JsonSerializer serializer) {
serializer.Serialize(writer, value.Item);
}
public static DataItem ReadJson(JsonReader reader, DataItem existingValue, JsonSerializer serializer) {
DataItem result = new DataItem();
result.Item = serializer.Deserialize<Dictionary<string, string>>(reader);
return result;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class CustomJsonSerializableAttribute : Attribute {
public readonly string Read;
public readonly string Write;
public CustomJsonSerializableAttribute()
: this(null, null) {
}
public CustomJsonSerializableAttribute(string read, string write) {
this.Read = read;
this.Write = write;
}
}
public class CustomJsonSerializableConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return objectType.GetCustomAttribute(typeof(CustomJsonSerializableAttribute), false) != null;
}
public override bool CanWrite {
get {
return true;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
if(value != null) {
var t = value.GetType();
var attr = (CustomJsonSerializableAttribute)t.GetCustomAttribute(typeof(CustomJsonSerializableAttribute), false);
var #delegate = t.GetMethod(attr.Write ?? "WriteJson", new Type[] { typeof(JsonWriter), t, typeof(JsonSerializer) });
#delegate.Invoke(null, new object[] { writer, value, serializer });
} else {
serializer.Serialize(writer, null);
}
}
public override bool CanRead {
get {
return true;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
var t = existingValue.GetType();
var attr = (CustomJsonSerializableAttribute)t.GetCustomAttribute(typeof(CustomJsonSerializableAttribute), false);
var #delegate = t.GetMethod(attr.Read ?? "ReadJson", new Type[] { typeof(JsonReader), t, typeof(JsonSerializer) });
return #delegate.Invoke(null, new object[] { reader, existingValue, serializer });
}
}

Related

How to skip property name in json serialization?

I am trying to convert List to json. Structure is as follow:
public class ResourceCollection
{
public string Name { get; set; }
public Resources Resources { get; set;}
}
public class Resources
{
public string en { get; set; }
}
List<ResourceCollection> liResourceName = new List<ResourceCollection>();
//section to add the objects in list
string json = JsonConvert.SerializeObject(liResourceName, Newtonsoft.Json.Formatting.Indented);
This is producing the result as expected:
[
{
"Name": "Hello",
"Resources":
{
"en": "Hello"
}
},
{
"Name": "World",
"Resources":
{
"en": "World"
}
}
]
How can I get the results like:-
{
"Hello": {
"en": "Hello"
},
"World": {
"en": "World"
}
}
You will need to create a custom JsonConverter that knows how to handle serializing ResourceCollection
public class ResourceCollectionConverter : JsonConverter<List<ResourceCollection>> {
public override bool CanRead {
get {
return false; //because ReadJson is not implemented
}
}
public override List<ResourceCollection> ReadJson(JsonReader reader, Type objectType, List<ResourceCollection> existingValue, bool hasExistingValue, JsonSerializer serializer) {
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, List<ResourceCollection> value, JsonSerializer serializer) {
var obj = new JObject(); // { }
foreach (var item in value) {
//{ "Hello" : { "en": "Hello" } }
obj[item.Name] = JObject.FromObject(item.Resources);
}
obj.WriteTo(writer);
}
}
Use the converter so that JsonConvert knows how to handle the serialization.
For example
List<ResourceCollection> liResourceName = new List<ResourceCollection>();
liResourceName.Add(new ResourceCollection { Name = "Hello", Resources = new Resources { en = "Hello" } });
liResourceName.Add(new ResourceCollection { Name = "World", Resources = new Resources { en = "World" } });
var formating = Newtonsoft.Json.Formatting.Indented;
var converter = new ResourceCollectionConverter();
string json = JsonConvert.SerializeObject(liResourceName, formating , converter);

C# Json.Net Deserialize Json with different "key" parameter

I am trying to deserialize JSON using the Json.NET library. JSON which I receive looks like:
{
"responseHeader": {
"zkConnected": true,
"status": 0,
"QTime": 2
},
"suggest": {
"mySuggester": {
"Ext": {
"numFound": 10,
"suggestions": [
{
"term": "Extra Community",
"weight": 127,
"payload": ""
},
{
"term": "External Video block",
"weight": 40,
"payload": ""
},
{
"term": "Migrate Extra",
"weight": 9,
"payload": ""
}
]
}
}
}
}
The problem is that the "Ext" that you can see in it is part of the parameter passed in the query string and will always be different. I want to get only the values assigned to the term "term".
I tried something like this, but unfortunately does not works:
public class AutocompleteResultsInfo
{
public AutocompleteResultsInfo()
{
this.Suggest = new Suggest();
}
[JsonProperty("suggest")]
public Suggest Suggest { get; set; }
}
public class Suggest
{
[JsonProperty("mySuggester")]
public MySuggesterElement MySuggesterElement { get; set; }
}
public struct MySuggesterElement
{
public MySuggester MySuggester;
public string JsonString;
public static implicit operator MySuggesterElement(MySuggester MySuggester) =>new MySuggesterElement { MySuggester = MySuggester };
public static implicit operator MySuggesterElement(string String) => new MySuggesterElement { JsonString = String };
}
public class MySuggester
{
[JsonProperty("suggestions")]
public Suggestions[] Suggestions { get; set; }
}
public class Suggestions
{
[JsonProperty("term")]
public string Autocopmplete { get; set; }
}
internal class SuggesterElementConverter : JsonConverter
{
public override bool CanConvert(Type t)
{
return t == typeof(MySuggesterElement) || t == typeof(MySuggesterElement?);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new MySuggesterElement { JsonString = stringValue };
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<MySuggester>(reader);
return new MySuggesterElement { MySuggester = objectValue };
}
throw new Exception("Cannot unmarshal type MySuggesterElement");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (MySuggesterElement)untypedValue;
if (value.JsonString != null)
{
serializer.Serialize(writer, value.JsonString);
return;
}
if (value.MySuggester != null)
{
serializer.Serialize(writer, value.MySuggester);
return;
}
throw new Exception("Cannot marshal type CollationElements");
}
public static readonly SuggesterElementConverter Singleton = new SuggesterElementConverter();
}
public class AutocompleteConverter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
SuggesterElementConverter.Singleton
}
};
}
var results = JsonConvert.DeserializeObject<AutocompleteResultsInfo>(resultJson, AutocompleteConverter.Settings);
Many thanks for your help.
Kind Regerds,
Wojciech
You could decode the "mySuggester" as a dictionary:
public class Suggest
public class Suggest
{
[JsonProperty("mySuggester")]
public Dictionary<string, MySuggester> MySuggester { get; set; }
}
Then you'll be able to access the suggestions with the query string parameter:
var variablePropertyName = "Ext";
var result = JsonConvert.DeserializeObject<AutocompleteResultsInfo>(_json);
var suggestions = result.Suggest.MySuggester[variablePropertyName].Suggestions;
if you don't know the property name you could also look it up in the dictionary:
var variablePropertyName = result.Suggest.MySuggester.Keys.First();
Working example:
https://dotnetfiddle.net/GIKwLs
If you don't need to deserialize the whole json string you can use a JsonTextReader. Example:
private static IEnumerable<string> GetTerms(string json)
{
using (JsonTextReader reader = new JsonTextReader(new StringReader(json)))
{
while (reader.Read())
{
if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("term"))
{
string term = reader.ReadAsString();
yield return term;
}
}
}
}
Using the code:
string json = #"{
""responseHeader"": {
""zkConnected"": true,
""status"": 0,
""QTime"": 2
},
""suggest"": {
""mySuggester"": {
""Ext"": {
""numFound"": 10,
""suggestions"": [
{
""term"": ""Extra Community"",
""weight"": 127,
""payload"": """"
},
{
""term"": ""External Video block"",
""weight"": 40,
""payload"": """"
},
{
""term"": ""Migrate Extra"",
""weight"": 9,
""payload"": """"
}
]
}
}
}
}";
IEnumerable<string> terms = GetTerms(json);
foreach (string term in terms)
{
Console.WriteLine(term);
}
If you only need the object containing the term, and nothing else,
you could work with the JSON values directly by using the JObject interface in JSON.Net.
var parsed = JObject.Parse(jsonString);
var usingLinq = (parsed["suggest"]["mySuggester"] as JObject)
.Descendants()
.OfType<JObject>()
.Where(x => x.ContainsKey("term"));
var usingJsonPath = parsed.SelectTokens("$.suggest.mySuggester.*.*[?(#.term)]")
.Cast<JObject>();

Serialize IEnumerable<IDictionary<string, object>> doesn't work with DefaultNamingStrategy

By default I use CamelCasePropertyNamesContractResolver in my project for serialize to json. But I want to change this strategy for one property.
public class ViewTable
{
public int Id { get; set; }
[JsonProperty(NamingStrategyType = typeof(DefaultNamingStrategy), ItemTypeNameHandling = TypeNameHandling.None, TypeNameHandling = TypeNameHandling.None)]
public IEnumerable<IDictionary<string, object>> Rows { get; set; }
}
So when I serialize this object I expect to get such json:
"result": {
"id": 15,
"rows": [
{
"SessionData_Department": "",
"SystemData_SerialNumber": "1"
}
]
}
But I got:
"result": {
"id": 15,
"Rows": [ //There must be a lowercase!!!
{
"sessionData_Department": "", //There must be a uppercase!!!
"systemData_SerialNumber": "1"
}
]
}
I have such json settings in my project:
var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.TypeNameHandling = TypeNameHandling.Auto;
How can I tell json serializer use DefaultNamingStrategy for IDictionary?
I'm not sure that there is a setting from out-of-the box. But you can do it by extending JsonConverter and using DefaultContractResolver:
public class RowsConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var settings = new JsonSerializerSettings()
{
ContractResolver = new DefaultContractResolver()
};
writer.WriteRawValue(JsonConvert.SerializeObject(value, settings));
}
}
Then change Rows property to:
[JsonProperty(ItemConverterType = typeof(RowsConverter))]
public IEnumerable<IDictionary<string, object>> Rows { get; set; }
You will have to extend DefaultNamingStrategy:
public class CamelCaseDictionaryKeyNamingStrategy : DefaultNamingStrategy
{
public CamelCaseDictionaryKeyNamingStrategy() : base() { this.ProcessDictionaryKeys = true; }
public override string GetDictionaryKey(string key)
{
if (ProcessDictionaryKeys && !string.IsNullOrEmpty(key))
{
if (char.ToUpperInvariant(key[0]) != key[0])
{
var builder = new StringBuilder(key) { [0] = char.ToUpperInvariant(key[0]) };
return builder.ToString();
}
}
return key;
}
}
Then use it like:
IDictionary<string, object> idict = new Dictionary<string, object>();
idict.Add("sessionData_Department", "1");
idict.Add("systemData_SerialNumber", "1");
IEnumerable<IDictionary<string, object>> row = new List<IDictionary<string, object>> { idict };
var val = new ViewTable
{
Id = 15,
Rows = row
};
var cc = new CamelCasePropertyNamesContractResolver
{
NamingStrategy = new CamelCaseDictionaryKeyNamingStrategy()
};
JsonSerializerSettings config = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = cc,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto
};
string js = JsonConvert.SerializeObject(val, config);
Output:
I have updated Rows to:
public class ViewTable
{
public int Id { get; set; }
[JsonProperty(PropertyName = "rows", NamingStrategyType = typeof(DefaultNamingStrategy), ItemTypeNameHandling = TypeNameHandling.None, TypeNameHandling = TypeNameHandling.None)]
public IEnumerable<IDictionary<string, object>> Rows { get; set; }
}

C# parsing JSON array overwrites values on the list

Im having problem with deserializing json file into my class.
Json file looks like this:
{
"DataFile": {
"header":{
"version": "123",
"date": "01.01.01",
},
"customer": {
"fID": "12-35-58",
"nameCust": "CompanyName",
"adressCust":{
"zip": "0000",
"city": "Foovile",
"streetNr": "1",
"post": "FoovilePost",
"street": "DunnoStr",
},
},
"content": [
{
"invoice":{
"DFID":"538",
},
"invoice":{
"DFID":"500",
},
"invoice":{
"DFID":"550",
},
"receipt":{
"DFID":"758",
},
"receipt":{
"DFID":"75",
},
}
],
}
}
Everything before content array deserializes fine,so to keep things clear I'll skip parts of my class. Relevant bit looks like this:
class DFFile
{
public DF Df { get; set; }
}
class DF
{
public Header header { get; set; }
public Customer customer { get; set; }
public List<DFContent> content { get; set; }
}
class DFContent
{
public Invoice invoice { get; set; }
public Receipt receipt { get; set; }
}
class Invoice
{
public int DFID { get; set; }
}
class Receipt
{
public int DFID { get; set; }
}
And i deserialize into DFFile instance like this:
DFFile sample = JsonConvert.DeserializeObject<DFFile>(json);
My problem is that it deserializes without errors but sample.DF.content have only one element that have invoice and receipt with last id of each type. And result I'm looking for is list where there is new element for each item of json content array.
I can change my class but way this json is build is set in stone, can't do anything about it and have to deal with it.
Is there any way to stop it changing last element of content and add new one instead?
Your content is set up to hold an array but you only have one item in it which has three invoice and two receipt values. Assuming you want your DFContent to hold either a receipt or invoice, change it to look like this:
"content": [
{"invoice":{
"DFID":"538",
}},
{"invoice":{
"DFID":"500",
}},
{"invoice":{
"DFID":"550",
}},
{"receipt":{
"DFID":"758",
}},
{"receipt":{
"DFID":"75",
}}
],
Since you cannot change the JSON I assumed that you can change your class structure.
Please consider this classes:
class DF
{
// Other properties...
[JsonConverter(typeof(FunnyListConverter))]
public List<Content> content { get; set; }
}
class Content
{
public int DFID { get; set; }
}
class Invoice : Content
{
}
class Receipt : Content
{
}
And here is the FunnyListConverter:
public class FunnyListConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var content = new List<Content>();
var currentType = "";
Invoice currentInvoice = null;
Receipt currentReceipt = null;
while (reader.Read())
{
if (reader.TokenType == JsonToken.EndArray)
{
break;
}
if (reader.Value?.ToString() == "invoice")
{
currentInvoice = new Invoice();
currentType = "invoice";
}
if (reader.Value?.ToString() == "receipt")
{
currentReceipt = new Receipt();
currentType = "receipt";
}
if (reader.Path.Contains("DFID") && reader.Value?.ToString() != "DFID")
{
switch (currentType)
{
case "invoice":
currentInvoice.DFID = int.Parse(reader.Value.ToString());
content.Add(currentInvoice);
currentInvoice = null;
break;
case "receipt":
currentReceipt.DFID = int.Parse(reader.Value.ToString());
content.Add(currentReceipt);
currentReceipt = null;
break;
}
}
}
return content;
}
public override bool CanConvert(Type objectType)
{
return true;
}
if (reader.Value?.ToString() == "receipt")
{
currentReceipt = new Receipt();
currentType = "receipt";
}
if (reader.Path.Contains("DFID") && reader.Value?.ToString() != "DFID")
{
switch (currentType)
{
case "invoice":
currentInvoice.DFID = int.Parse(reader.Value.ToString());
content.Add(currentInvoice);
currentInvoice = null;
break;
case "receipt":
currentReceipt.DFID = int.Parse(reader.Value.ToString());
content.Add(currentReceipt);
currentReceipt = null;
break;
}
}
}
return content;
}
public override bool CanConvert(Type objectType)
{
return true;
}
}

Creating JSON with dynamic keys in c#

I am trying to replicate the following JSON structure:
{"result_content": {
"data": {
"city_name" : "Beverly Hills",
"2014-06-05T00:00:00": {
"morning_low": "20",
"daytime_high": "40"
},
"2014-06-06T00:00:00": {
"morning_low": "21",
"daytime_high": "41"
},
"2014-06-07T00:00:00": {
"morning_low": "22",
"daytime_high": "42"
},
"2014-06-08T00:00:00": {
"morning_low": "23",
"daytime_high": "43"
},
"2014-06-09T00:00:00": {
"morning_low": "24",
"daytime_high": "44"
}
}
}
}
But I can't figure out how create the keys to be dynamic using C#.
Here are my object class's
public class Day
{
public string morning_low { get; set; }
public string daytime_high { get; set; }
}
public class Data
{
public string city_name { get; set; }
public List<Day> days { get; set; }
}
public class ResultContent
{
public Data data { get; set; }
}
And here is how im building it all:
ResultContent content = new ResultContent();
content.data = new Data();
content.data.city_name = results.Body.GetCityForecastByZIPResponse.GetCityForecastByZIPResult.City;
foreach (Forecast day in results.Body.GetCityForecastByZIPResponse.GetCityForecastByZIPResult.ForecastResult.Forecast){
Day x = new Day();
x.daytime_high = day.Temperatures.DaytimeHigh;
x.morning_low = day.Temperatures.MorningLow;
content.data.days.Add(x);
}
return JsonConvert.SerializeObject(content);
This just returns a JSON array of days which is not what I want. I have the DateTime in my results object.
This is probably what you're looking for in that case...
void Main()
{
Result result = new Result
{
Data = new Data
{
WeatherData = new List<City>
{
new City
{
Name = "London",
Temp = new Dictionary<DateTime, TemperatureRange>
{
{
DateTime.UtcNow,
new TemperatureRange
{
DayHigh = 0,
MorningLow = 50
}
}
}
}
}
}
};
JsonConvert.SerializeObject(result);
}
public class Result
{
[JsonProperty("result_content")]
public Data Data { get; set; }
}
public class Data
{
[JsonProperty("data")]
public List<City> WeatherData { get; set; }
}
public class City
{
[JsonProperty("city_name")]
public string Name { get; set; }
public Dictionary<DateTime, TemperatureRange> Temp { get; set; }
}
public class TemperatureRange
{
public int MorningLow { get; set; }
public int DayHigh { get; set; }
}
If you really want such a sophisticated format, I'd go with a custom JsonConverter:
public class Day
{
public string morning_low { get; set; }
public string daytime_high { get; set; }
}
[JsonConverter(typeof(Data.Converter))]
public class Data
{
public string city_name { get; set; }
public Dictionary<DateTime, Day> days { get; set; }
public class Converter : JsonConverter
{
public override bool CanConvert(Type type) { return type == typeof(Data); }
public override object ReadJson(JsonReader reader, Type type, object value, JsonSerializer serializer)
{
Data obj = new Data();
obj.days = new Dictionary<DateTime, Day>();
DateTime v;
while (reader.Read() && reader.TokenType != JsonToken.EndObject)
{
if (reader.TokenType != JsonToken.PropertyName)
throw new JsonSerializationException("Unexpected token type");
if ("city_name" == (string)reader.Value)
{
if (obj.city_name != null)
throw new JsonSerializationException("Duplicate key: city_name");
obj.city_name = reader.ReadAsString();
}
else if (DateTime.TryParseExact((string)reader.Value, serializer.DateFormatString,
serializer.Culture, DateTimeStyles.None, out v))
{
reader.Read();
obj.days.Add(v, serializer.Deserialize<Day>(reader));
}
else
{
if (serializer.MissingMemberHandling == MissingMemberHandling.Error)
throw new JsonSerializationException("Unexpected property: " + reader.Value);
reader.Read();
serializer.Deserialize(reader, reader.ValueType);
}
}
return obj;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Data obj = (Data)value;
writer.WriteStartObject();
writer.WritePropertyName("city_name");
writer.WriteValue(obj.city_name);
foreach (var pair in obj.days)
{
writer.WritePropertyName(pair.Key.ToString(serializer.DateFormatString));
serializer.Serialize(writer, pair.Value);
}
writer.WriteEndObject();
}
}
}
public class ResultContent
{
public Data data { get; set; }
}
public class ResultContentRoot
{
public ResultContent result_content { get; set; }
}
public static void Main()
{
var data = new Data();
data.city_name = "New York";
data.days = new Dictionary<DateTime, Day>();
data.days.Add(DateTime.Today, new Day() { morning_low = "24", daytime_high = "29" });
var result_content = new ResultContent();
result_content.data = data;
var root = new ResultContentRoot();
root.result_content = result_content;
var s = JsonConvert.SerializeObject(root);
}
I think it is the only way to mix dictionary and object contracts.
If you only need one-way serialization, you may also go with dynamic. It takes less code:
public class Day
{
public string morning_low { get; set; }
public string daytime_high { get; set; }
}
public class ResultContent
{
public dynamic data { get; set; }
}
public class ResultContentRoot
{
public ResultContent result_content { get; set; }
}
public static void Main()
{
dynamic data = new ExpandoObject();
data.city_name = "New York";
IDictionary<string, object> days = (IDictionary<string, object>)data;
days.Add(DateTime.Today.ToString("yyyy-MM-dd'T'HH:mm:ss"), new Day() { morning_low = "24", daytime_high = "29" });
var result_content = new ResultContent();
result_content.data = data;
var root = new ResultContentRoot();
root.result_content = result_content;
var s = JsonConvert.SerializeObject(root);
}
But it is very close to discard all that strong typing and just construct response with JObjects.
I think it should be an array of days and representing it the way you asked wouldn't be good, because creating a dynamic json format is difficult to parse.
They way you defined it should produce something like this below.
{
"result_content": {
"data": {
"city_name" : "Beverly Hills",
"days" :
[
{
"morning_low": "20",
"daytime_high": "40"
},
{
"morning_low": "21",
"daytime_high": "41"
},
{
"morning_low": "22",
"daytime_high": "42"
},
{
"morning_low": "23",
"daytime_high": "43"
},
{
"morning_low": "24",
"daytime_high": "44"
}
]
}
}
}
What you're missing is the day itself, which should be defined in your Day class. Add it to get:
"days" :
[
{
"day" : "2014-06-05T00:00:00",
"morning_low": "20",
"daytime_high": "40"
}
...
]

Categories

Resources