Reputation: 5762
I have a JSON in the following format (from couchDB view)
{"rows":[
{"key":["2015-04-01","524","http://www.sampleurl.com/"],"value":1},
{"key":["2015-04-01","524","http://www.sampleurl2.com/"],"value":2},
{"key":["2015-04-01","524","http://www.sampleurl3.com"],"value":1}
]}
I need to create a "service" to get this data from couchDB and insert it on SQL Server (for generating reports..) in a efficient way. My first bet was to bulk insert this json into SQL Server, like this: Bulk Insert from Generic List into SQL Server with minimum lines of code
The problem is, how can I map this JSON into a c# class?
Ultil now this is what I did:
public class Row
{
public List<string> key { get; set; }
public int value { get; set; }
}
public class RootObject
{
public List<Row> rows { get; set; }
}
var example = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(jsontext);
This gives me a list of "Rows". Each row has a Key and each key is an array containing a Date, Url and a number.
I could loop through the "rows" and create the objects on my own but this doens't sound very performant to me. Also, the JSON will be big, something like 5MB more or less.
The structure that I want is something like this:
public class Click
{
public DateTime Date { get; set; }
public string Code { get; set; }
public string Url { get; set; }
public int Count { get; set; }
}
How can I extract the "key" array and map it into separated properties. This way, I wouldn't need a for loop.
Any ideas?
Upvotes: 1
Views: 741
Reputation: 116980
You could create a custom JsonConverter
for this:
[JsonConverter(typeof(ClickConverter))]
public class Click
{
public DateTime Date { get; set; }
public string Code { get; set; }
public string Url { get; set; }
public int Count { get; set; }
}
public class ClickConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Click).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token == null || token.Type == JTokenType.Null)
return null;
var click = (existingValue as Click ?? new Click());
var key = token["key"] as JArray;
if (key != null && key.Count > 0)
click.Date = (DateTime)key[0];
if (key != null && key.Count > 1)
click.Code = (string)key[1];
if (key != null && key.Count > 2)
click.Url = (string)key[2];
var value = token["value"];
if (value != null)
click.Count = (int)value;
return click;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Fill in with the opposite of the code above, if needed
var click = value as Click;
if (click == null)
writer.WriteNull();
else
serializer.Serialize(writer,
new
{
// Update the date string format as appropriate
// https://msdn.microsoft.com/en-us/library/8kb3ddd4%28v=vs.110%29.aspx
key = new string[] { click.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), click.Code.ToString(CultureInfo.InvariantCulture), click.Url },
value = click.Count
});
}
}
public class RootObject
{
public List<Click> rows { get; set; }
}
Having applied the converter directly to the class, you can (de)serialize as usual:
var jsontext = @"{""rows"":[
{""key"":[""2015-04-01"",""524"",""http://www.sampleurl.com/""],""value"":1},
{""key"":[""2015-04-01"",""524"",""http://www.sampleurl2.com/""],""value"":2},
{""key"":[""2015-04-01"",""524"",""http://www.sampleurl3.com""],""value"":1}
]}";
var rows = JsonConvert.DeserializeObject<RootObject>(jsontext);
Debug.WriteLine(JsonConvert.SerializeObject(rows, Formatting.Indented));
Upvotes: 2
Reputation: 5953
Here you are.
public class Row
{
// Serialize/Deserialize the `key` into it's values.
public List<string> key { get { return new List<string>() { Date.ToString("yyyy-MM-dd"), Code, Url }; } set { Date = DateTime.Parse(value[0]); Code = value[1]; Url = value[2]; } }
// Serialize/Deserialize the `value` into `Count`.
public int value { get { return Count; } set { Count = value; } }
[ScriptIgnore]
public DateTime Date { get; set; }
[ScriptIgnore]
public string Code { get; set; }
[ScriptIgnore]
public string Url { get; set; }
[ScriptIgnore]
public int Count { get; set; }
public override string ToString()
{
return Date.ToString("yyyy-MM-dd") + ", " + Code + ", " + Url + ", " + Count;
}
}
public class RootObject
{
public List<Row> rows { get; set; }
}
public static void _Main(string[] args)
{
string json = "{\"rows\":[" +
"{\"key\":[\"2015-04-01\",\"524\",\"http://www.sampleurl.com/\"],\"value\":1}," +
"{\"key\":[\"2015-04-01\",\"524\",\"http://www.sampleurl2.com/\"],\"value\":2}," +
"{\"key\":[\"2015-04-01\",\"524\",\"http://www.sampleurl3.com\"],\"value\":1}" +
"]}";
var jss = new JavaScriptSerializer();
var example = jss.Deserialize<RootObject>(json);
foreach (Row r in example.rows)
{
Console.WriteLine(r.ToString());
}
}
It should be self-explanatory. If you want me to go into detail just ask. Though, this does require the elements to always be in a consistent order.
Output of the above:
2015-04-01, 524, http://www.sampleurl.com/, 1
2015-04-01, 524, http://www.sampleurl2.com/, 2
2015-04-01, 524, http://www.sampleurl3.com, 1
The obvious benefit to this method is it has a low-overhead. It's quite easy to maintain as well. It also means that you could obviously provide an XmlIgnoreAttribute
on the properties that have the ScriptIgnoreAttribute
on them, and it would also produce and read valid Serialized XML.
Note: I used the System.Web.Script.Serialization.JavaScriptSerializer
. If JSON.NET
does not use the ScriptIgnoreAttribute
, then you'll have to apply whatever attribute it does use. (I've never used JSON.NET
.)
Another Note: I wrote this against C#6.0
and .NET 4.6
. Your results may vary.
Upvotes: 2