deserialize json to 2d array - c#

Does anyone know how to convert a string which contains json that has no parameter names into a C# array. My code receives json from RESTApi That looks like this
[["name","surname","street","phone"],
["alex","smith","sky blue way","07747233279"],
["john","patterson","richmond street","07658995465"]]
Every example I have seen here involved parameter names and their json looked like this
[Name:"alex",Surname:"smith",street:"sky blue way",phone:"07747233279"],
[Name:"john",Surname:"patterson",street:"richmond street",phone:"07658995465"]]
I am trying to use JavaScriptSerializer but i don't know how to properly maintain a Class for such type of JSON

This is not quite what the OP is asking (2d array), but my approach to this problem.
It seems that you have a collection of people, so I'd create a Person class like this:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public string Street { get; set; }
public string Phone { get; set; }
}
And then a parser class that takes the json string as a parameter, and returns a collection of Person:
public class PersonParser
{
public IEnumerable<Person> Parse(string content)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
if (string.IsNullOrWhiteSpace(content))
{
yield break;
}
// skip 1st array, which contains the property names
var values = JsonConvert.DeserializeObject<string[][]>(content).Skip(1);
foreach (string[] properties in values)
{
if (properties.Length != 4)
{
// ignore line? thrown exception?
// ...
continue;
}
var person = new Person
{
Name = properties[0],
Surname = properties[1],
Street = properties[2],
Phone = properties[3]
};
yield return person;
}
}
}
Using the code:
string weirdJson = #"[[""name"",""surname"",""street"",""phone""],
[""alex"",""smith"",""sky blue way"",""07747233279""],
[""john"",""patterson"",""richmond street"",""07658995465""]]";
var parser = new PersonParser();
IEnumerable<Person> people = parser.Parse(weirdJson);
foreach (Person person in people)
{
Console.WriteLine($"{person.Name} {person.Surname}");
}

You could do this, which will give you a list of list of string
var input = "[[\"name\", \"surname\", \"street\", \"phone\"],\r\n\t[\"alex\", \"smith\", \"sky blue way\", \"07747233279\"],\r\n\t[\"john\", \"patterson\", \"richmond street\", \"07658995465\"]]";
var results = JsonConvert.DeserializeObject<List<List<string>>>(input);
foreach (var item in results)
Console.WriteLine(string.Join(", ", item));
Output
name, surname, street, phone
alex, smith, sky blue way, 07747233279
john, patterson, richmond street, 07658995465
Full Demo Here

Related

File parsing to get specific information

I have file which looks like this:
-- Name John Smith, PhD
[20,00] Title : Software Engineer
[20,00] Employee Id : 20307
[20,00] Level : 41
[20,00] Start Date : 04/21/2014
[20,00] Org : Security
Every file contains an entry for just 1 person. I need to extract the name, title and level from this file then create and populate an object of the following class:
public class Person
{
public string Name { get; set; }
public string Title { get; set; }
public string Level { get; set; }
}
One way to do it is I create a list of strings which need to be matched:
List<string> properties = new List<string> { "Name", "Title", "Level" };
Then read the file line by line and try to find a match like:
properties.Any(x => line.Contains(x))
If I find a match, I do some string split and parsing to get the values that I need. But this will involve a lot of manual work. Is there a way I can map the strings to a variable of the class and do this parsing?
What I mean is something like this:
Person person = new Person();
Dictionary<string, Object> FieldToDataMember = new Dictionary<string, Object>()
{
{"Name", person.Name},
{"Title", person.Title},
{"Level", person.Level}
};
Now I read the file line by line, if it matches one of the keys, I do the parsing and it directly updates the value of the corresponding variable. This way, I don't need to first find whether there's a match and then again check which string it matched to be able to put it in the right variable. Is something like this possible?
Appreciate your help. Thanks!
Edit: I would also like to exit the loop (foreach (string line in file)) and stop reading the file further after I find all the properties I'm looking for.
One way to do this using a collection of property name strings is to use reflection to get the property and set the value. This requires extra overhead compared to setting the properties directly, but it is fewer lines of code as you were asking for.
We can use a dictionary or a list of tuples (or a custom class) to map the string in the file with the actual property name (in cases like "Start Date" and StartDate).
Here's an example where I added a public static Person FromFile(string filePath) method, which will take in a file path and return a new Person with properties set from the contents of the file.
It works by first determining if any of the property names in the string array are contained in a file line. If one is, then it uses some logic based on your file sample to get the value for that property, and then uses reflection to set the property value of a Person object:
public class Person
{
public string Name { get; set; }
public string Title { get; set; }
public string Level { get; set; }
public string StartDate { get; set; }
private class FileToPropertyMap
{
public string FileValue { get; }
public string PropertyName { get; }
public bool IsSet { get; set; }
public FileToPropertyMap(string fileValue, string propertyName)
{
FileValue = fileValue;
PropertyName = propertyName;
}
}
public static Person FromFile(string filePath)
{
if (!File.Exists(filePath)) throw new FileNotFoundException(nameof(filePath));
var person = new Person();
var propertyMap = new List<FileToPropertyMap>
{
new FileToPropertyMap("Name", "Name"),
new FileToPropertyMap("Title", "Title"),
new FileToPropertyMap("Level", "Level"),
new FileToPropertyMap("Start Date", "StartDate"),
};
foreach (var line in File.ReadLines(filePath))
{
// Find a match for one of the properties
var match = propertyMap.FirstOrDefault(p => line.Contains(p.FileValue));
if (match == null) continue;
// Get the value of the property from the file line
var value = line.Substring(line.IndexOf(match.FileValue) +
match.FileValue.Length).Trim();
if (value.Contains(':')) value = value.Split(':')[1].Trim();
// Set the property value using reflection
person.GetType().GetProperty(match.PropertyName).SetValue(person, value);
// Mark this property as "IsSet"
match.IsSet = true;
// If we've set all the properties, exit the loop
if (propertyMap.All(p => p.IsSet)) break;
}
return person;
}
}
In use, this would look something like:
Person myPerson = Person.FromFile("#c:\Public\PeopleFiles\JohnSmith.txt");
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using System.IO;
namespace ConsoleApplication167
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
List<Person> people = new List<Person>();
StreamReader reader = new StreamReader(FILENAME);
string line = "";
Person person = null;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length > 0)
{
if (line.StartsWith("-- Name"))
{
person = new Person();
people.Add(person);
person.Name = line.Replace("-- Name", "").Trim();
}
else
{
string pattern = "](?'key'[^:]+):(?'value'.*)";
Match match = Regex.Match(line, pattern);
string key = match.Groups["key"].Value.Trim();
string value = match.Groups["value"].Value.Trim();
switch (key)
{
case "Title" :
person.Title = value;
break;
case "Level":
person.Level = value;
break;
}
}
}
}
}
}
public class Person
{
public string Name { get; set; }
public string Title { get; set; }
public string Level { get; set; }
}
}

How to fill properties of objects using LINQ and return collection

I have file with lines like this: 05122018;surname1;ItemName1;Price1
And OrderModel
public class OrderModel
{
public string ManagerSurname { get; set; }
public DateTime Date { get; set; }
public string CustomerSurname { get; set; }
public string ItemName { get; set; }
public double Price { get; set; }
}
How to make LINQ request to return collection of OrderModels ?
public IEnumerable<OrderModel> Parse(string[] input)
{
return
(input ?? throw new ArgumentNullException(nameof(input)))
.Select(orderModel => new OrderModel()
{
//filling objects
}).Where(orderModel => //Any);
Considering the input[] array consists of one line
05122018;surname1;ItemName1;Price1
for each entry and each line has the same order of items. You could do a split in the select, to get the appropriate values:
public IEnumerable<OrderModel> Parse(string[] input)
{
return
(input ?? throw new ArgumentNullException(nameof(input)))
.Select(orderModel =>
{
var items = orderModel.Split(";");
if (items.Length != 4)
{
throw new ArgumentException();
}
return new OrderModel()
{
//filling objects
ManagerSurname = items[1],
ItemName = items[2],
...
};
}).Where(orderModel => //Any);
}
Basically, you look like you want to deserialize the string that's coming in from semicolon delimited to an object. That's a pretty ugly and potentially fragile way to serialize / deserialize your data, and if you can change it (to JSON, or XML or something else) it might be a good idea.
But you could create a constructor for your OrderModel class that takes the serialized string as a parameter and deserializes in there:
public OrderModel(string inOrderModel){
var splitString = inOrderModel.Split(';');
ManagerSurname = splitString[1];
// etc
}
And then you can use LINQ to create the list from the incoming array:
return input.Select(i => new OrderModel(i));
returning simply return _context.OrderModel will return your entire OrderModel from the context:
public IEnumerable<OrderModel> Parse(string[] input)
{
(input ?? throw new ArgumentNullException(nameof(input)))
return _context.OrderModel;
}
or assuming you would like to filter your dataset by data in the input string array, you can filter with input.contains(model.field). Example filters Item Name based on the input string array:
public IEnumerable<OrderModel> Parse(string[] input)
{
(input ?? throw new ArgumentNullException(nameof(input)))
return _context.OrderModel.Where(m => input.contains(m.ItemName));
}

Print object with nested properties as string of comma separated key-value pairs

I have a Person class:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
}
As an end result, I would like to print out a Person instance as JSON, but I'd like it to be a huge string of key-value pairs, e.g.:
"Name:John,Surname:Doe,Line1:Infinite Loop,Line2:California"
Notice that in the example above, I got rid of the actual class names (i.e. it prints out Line1 instead of Address.Line1) - i only care about all the name/value pairs.
So the end result would be an array of Persons:
"persons":[
"Name:John,Surname:Doe,Line1:Infinite Loop 1,Line2:California",
"Name:Jane,Surname:Doe,Line1:Infinite Loop 2,Line2:California"
]
As a starting point, I tried using reflection:
void Main()
{
var persons = new List<Person>();
persons.Add(new Person
{
Name = "John",
Surname = "Doe",
Address = new Address
{
Line1 = "Infinite Loop",
Line2 = "California"
}
});
foreach(var person in persons)
{
var properties = new List<string>();
foreach(var property in person.GetType().GetProperties())
{
properties.Add($"{property.Name}:{property.GetValue(person, null)}");
}
Console.WriteLine(string.Join(",", properties));
}
}
But I get the following output in LINQPad:
Name:John,Surname:Doe,Address:UserQuery+Address
I assume Address is not properly iterated upon because it's a nested object within Person. Even so, this doesn't look like the most clean/efficient approach.
You could do this using a custom JsonConverter like so:
class PersonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Person);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var pairs = JObject.FromObject(value)
.Descendants()
.OfType<JProperty>()
.Where(p => p.Value is JValue)
.Select(p => p.Name + ":" + p.Value);
writer.WriteValue(string.Join(",", pairs));
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it by passing the converter to JsonConvert.SerializeObject like this:
string json = JsonConvert.SerializeObject(obj, Formatting.Indented, new PersonConverter());
Here is a working demo: https://dotnetfiddle.net/L4YDsm
Add a ToString override to your class and return a collection of strings as JSON.
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
public override string ToString()
{
return $"Name:{Name},Surname:{Surname},Line1:{Address?.Line1},Line2:{Address?.Line2}";
}
}
You must implement the reflection in a way that it could go deeper into the structure of your object in order to do what you want. Here's a simple recursive and generic adaptation to your provided code.
public static string GetRecursivePropertyValues(object obj)
{
var properties = new List<string>();
foreach (var property in obj.GetType().GetProperties())
{
object currentPropertyValue = property.GetValue(obj);
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
properties.Add($"{property.Name}:{currentPropertyValue}");
else
{
var subProperties = GetRecursivePropertyValues(currentPropertyValue);
properties.Add(subProperties);
}
}
return string.Join(";", properties);
}
This implementation first verifies if each property is defined with a primitive type or string (which are not considered primitive types in C#) and prints them normally if that's the case. Else, if the type is complex (like if it is declared as an Address instance), it recursivelly retrieves the properties of the complex object, and adds them to the resulting string.
You can call it like:
string propertiesStr = GetRecursivePropertyValues(person);
Edit: code now only flattens the object as required by OP (previous code sample used some rustic JSON formatting).
Using Newtonsoft.json
https://www.newtonsoft.com/json/help/html/SerializingJSON.htm
Check the above link... It will tell you how to serialize an object to json, it's important to note, you will want to add all your items to a list, then serialize the list into json to get the desired effect
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
There example:
string output = JsonConvert.SerializeObject(product);
//{
// "Name": "Apple",
// "ExpiryDate": "2008-12-28T00:00:00",
// "Price": 3.99,
// "Sizes": [
// "Small",
// "Medium",
// "Large"
// ]
//}

convert json String to datatable?

While converting json string datatable facing an issue with , (comma) value in value field.
actualy my json string is [{"BNo":"345","GNo":"3453","FirstName":"fjai","LastName":"ljai","Address":"BARETI,CEVO, 13/2","Telephone":"051682247","BirthDate":"23-Jan-1981","Email":""}]
In that please look at the address scenario "Address":"BARETI,CEVO, 13/2"
It has the , in the values field. While converting the string to data base i got error. Here the code which i used convert json string to datatable
public DataTable JsonStringToDataTbl(string jsonString)
{
DataTable dt = new DataTable();
string[] jsonStringArray = Regex.Split(jsonString.Replace("[", "").Replace("]", ""), "},{");
List<string> ColumnsName = new List<string>();
foreach (string jSA in jsonStringArray)
{
string[] jsonStringData = Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
foreach (string ColumnsNameData in jsonStringData)
{
try
{
int idx = ColumnsNameData.IndexOf(":");
string ColumnsNameString = ColumnsNameData.Substring(0, idx - 1).Replace("\"", "");
if (!ColumnsName.Contains(ColumnsNameString))
{
ColumnsName.Add(ColumnsNameString);
}
}
catch (Exception ex)
{
throw new Exception(string.Format("Error Parsing Column Name : {0}", ColumnsNameData));
}
}
break;
}
foreach (string AddColumnName in ColumnsName)
{
dt.Columns.Add(AddColumnName);
}
foreach (string jSA in jsonStringArray)
{
string[] RowData = Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
DataRow nr = dt.NewRow();
foreach (string rowData in RowData)
{
try
{
int idx = rowData.IndexOf(":");
string RowColumns = rowData.Substring(0, idx - 1).Replace("\"", "");
string RowDataString = rowData.Substring(idx + 1).Replace("\"", "");
nr[RowColumns] = RowDataString;
}
catch (Exception ex)
{
continue;
}
}
dt.Rows.Add(nr);
}
return dt;
}
The code must omit the , in the value field.. what can i do
If your keys are unknown at the time of being read, then you can use the JObject and the JProperty classes from JSON.Net to retrieve the keys and their values like this:
private void printKeysAndValues(string json)
{
var jobject = (JObject)((JArray)JsonConvert.DeserializeObject(json))[0];
foreach (var jproperty in jobject.Properties())
{
Console.WriteLine("{0} - {1}", jproperty.Name, jproperty.Value);
}
}
Applied to two different JSON input string, retrieves the key/value pair:
var json1 = #"[{""BNo"":""345"",""GNo"":""3453"",""FirstName"":""fjai"",""LastName"":""ljai"",""Address"":""BARETI,CEVO, 13/2"",""Telephone"":""051682247"",""BirthDate"":""23-Jan-1981"",""Email"":""""}]";
var json2 = #"[{""Test"": ""A"", ""Text"":""some text"", ""Numbers"":""123""}]";
printKeysAndValues(json1);
Console.WriteLine("-------------------");
printKeysAndValues(json2);
And the output is:
BNo - 345
GNo - 3453
FirstName - fjai
LastName - ljai
Address - BARETI,CEVO, 13/2
Telephone - 051682247
BirthDate - 23-Jan-1981
Email -
-------------------
Test - A
Text - some text
Numbers - 123
One possibility would be to use the dynamic keyword. You can directly access the field like this:
var json = #"[{""BNo"":""345"",""GNo"":""3453"",""FirstName"":""fjai"",""LastName"":""ljai"",""Address"":""BARETI,CEVO, 13/2"",""Telephone"":""051682247"",""BirthDate"":""23-Jan-1981"",""Email"":""""}]";
dynamic data = JsonConvert.DeserializeObject(json);
// take the first element of the array
string address = data[0].Address;
Console.WriteLine(address.Replace(",", " "));
The output is:
BARETI CEVO 13/2
Note that String.Replace does not fail, if the symbol that should be replaced is not currently present, so "test".Replace(",", " "); will return test.
Another possibility is to use the in ASP.NET build in JSON converter (serializer/deserializer) - NewtonSoft JSON.Net. You can use it in order to regain the structured data. You need to create a class that represents the JSON structure:
public class Data
{
public string BNo { get; set; }
public string GNo { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Telephones { get; set; }
public string BirthDates { get; set; }
public string Emails { get; set; }
}
Then the current JSON can be converted to an object of type Data using the JsonConvert.DeserializeObject method:
var json = #"[{""BNo"":""345"",""GNo"":""3453"",""FirstName"":""fjai"",""LastName"":""ljai"",""Address"":""BARETI,CEVO, 13/2"",""Telephone"":""051682247"",""BirthDate"":""23-Jan-1981"",""Email"":""""}]";
// remove square braces [ and ] at the start resp. end
var data = JsonConvert.DeserializeObject<Data>(json.Substring(1).Substring(0, json.Length - 2));
Now you can access the Address field and for example replace the , symbol:
Console.WriteLine(data.Address.Replace(",", " "));
The output is:
BARETI CEVO 13/2
I think your service returns also the wrong JSON format. JSON always starts with an object (when not in JavaScript), meaning that everything at the top level must be enclosed within curly braces { and }. If the service should return an array, then it should look like this {"results": [{"BNo":"...},{...}]}. If you can't change the service, then you can adapt / correct the returned string. Add a typed model for the array:
public class DataHolder
{
public Data[] data { get; set; }
}
and then create a correct JSON object holding an array:
var data = JsonConvert.DeserializeObject<DataHolder>("{\"data\":" + json + "}");
Console.WriteLine(data.data[0].Address.Replace(",", " "));
The output is again the same.
You can convert the JSON value to C# objects using Newtonsoft. This would be easy for you. Once you have converted to the below object, you can easily modify the Address property to remove the ',' value.
public class RootObject
{
public string BNo { get; set; }
public string GNo { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Telephone { get; set; }
public string BirthDate { get; set; }
public string Email { get; set; }
}
Use the below line to convert to C# object
var jsonString = "The output of your webservice";
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObject>(jsonString);
Now obj instance holds the C# object which is very easy to manipulate.

JObject.Parse vs JsonConvert.DeserializeObject

What's the difference between JsonConvert.DeserializeObject and JObject.Parse? As far as I can tell, both take a string and are in the Json.NET library. What kind of situation would make one more convenient than the other, or is it mainly just preference?
For reference, here's an example of me using both to do exactly the same thing - parse a Json string and return a list of one of the Json attributes.
public ActionResult ReadJson()
{
string countiesJson = "{'Everything':[{'county_name':null,'description':null,'feat_class':'Civil','feature_id':'36865',"
+"'fips_class':'H1','fips_county_cd':'1','full_county_name':null,'link_title':null,'url':'http://www.alachuacounty.us/','name':'Alachua County'"+ ",'primary_latitude':'29.7','primary_longitude':'-82.33','state_abbreviation':'FL','state_name':'Florida'},"+
"{'county_name':null,'description':null,"+ "'feat_class':'Civil','feature_id':'36866','fips_class':'H1','fips_county_cd':'3','full_county_name':null,'link_title':null,'url':'http://www.bakercountyfl.org/','name':'Baker County','primary_latitude':'30.33','primary_longitude':'-82.29','state_abbreviation':'FL','state_name':'Florida'}]}";
//Can use either JSONParseObject or JSONParseDynamic here
List<string> counties = JSONParseObject(countiesJson);
JSONParseDynamic(countiesJson);
return View(counties);
}
public List<string> JSONParseObject(string jsonText)
{
JObject jResults = JObject.Parse(jsonText);
List<string> counties = new List<string>();
foreach (var county in jResults["Everything"])
{
counties.Add((string)county["name"]);
}
return counties;
}
public List<string> JSONParseDynamic(string jsonText)
{
dynamic jResults = JsonConvert.DeserializeObject(jsonText);
List<string> counties = new List<string>();
foreach(var county in jResults.Everything)
{
counties.Add((string)county.name);
}
return counties;
}
The LINQ-to-JSON API (JObject, JToken, etc.) exists to allow working with JSON without needing to know its structure ahead of time. You can deserialize any arbitrary JSON using JToken.Parse, then examine and manipulate its contents using other JToken methods. LINQ-to-JSON also works well if you just need one or two values from the JSON (such as the name of a county).
JsonConvert.DeserializeObject, on the other hand, is mainly intended to be used when you DO know the structure of the JSON ahead of time and you want to deserialize into strongly typed classes. For example, here's how you would get the full set of county data from your JSON into a list of County objects.
class Program
{
static void Main(string[] args)
{
string countiesJson = "{'Everything':[{'county_name':null,'description':null,'feat_class':'Civil','feature_id':'36865',"
+"'fips_class':'H1','fips_county_cd':'1','full_county_name':null,'link_title':null,'url':'http://www.alachuacounty.us/','name':'Alachua County'"+ ",'primary_latitude':'29.7','primary_longitude':'-82.33','state_abbreviation':'FL','state_name':'Florida'},"+
"{'county_name':null,'description':null,"+ "'feat_class':'Civil','feature_id':'36866','fips_class':'H1','fips_county_cd':'3','full_county_name':null,'link_title':null,'url':'http://www.bakercountyfl.org/','name':'Baker County','primary_latitude':'30.33','primary_longitude':'-82.29','state_abbreviation':'FL','state_name':'Florida'}]}";
foreach (County c in JsonParseCounties(countiesJson))
{
Console.WriteLine(string.Format("{0}, {1} ({2},{3})", c.name,
c.state_abbreviation, c.primary_latitude, c.primary_longitude));
}
}
public static List<County> JsonParseCounties(string jsonText)
{
return JsonConvert.DeserializeObject<RootObject>(jsonText).Counties;
}
}
public class RootObject
{
[JsonProperty("Everything")]
public List<County> Counties { get; set; }
}
public class County
{
public string county_name { get; set; }
public string description { get; set; }
public string feat_class { get; set; }
public string feature_id { get; set; }
public string fips_class { get; set; }
public string fips_county_cd { get; set; }
public string full_county_name { get; set; }
public string link_title { get; set; }
public string url { get; set; }
public string name { get; set; }
public string primary_latitude { get; set; }
public string primary_longitude { get; set; }
public string state_abbreviation { get; set; }
public string state_name { get; set; }
}
Notice that Json.Net uses the type argument given to the JsonConvert.DeserializeObject method to determine what type of object to create.
Of course, if you don't specify a type when you call DeserializeObject, or you use object or dynamic, then Json.Net has no choice but to deserialize into a JObject. (You can see for yourself that your dynamic variable actually holds a JObject by checking jResults.GetType().FullName.) So in that case, there's not much difference between JsonConvert.DeserializeObject and JToken.Parse; either will give you the same result.
JsonConvert.DeserializeObject has one advantage over JObject.Parse:
It is possible to use custom JsonSerializerSettings.
This can be very useful e.g. if you want to control how dates are deserialized.
By default dates are deserialized into DateTime objects.
This means that you may end up with a date with another time zone than the one in the json string.
You can change this behaviour by creating a JsonSerializerSetting and setting
DateParseHandling to DateParseHandling.DateTimeOffset.
An example:
var json = #"{ ""Time"": ""2015-10-28T14:05:22.0091621+00:00""}";
Console.WriteLine(json);
// Result: { "Time": "2015-10-28T14:05:22.0091621+00:00" }
var jObject1 = JObject.Parse(json);
Console.WriteLine(jObject1.ToString());
// Result: { "Time": "2015-10-28T15:05:22.0091621+01:00" }
var jObject2 = Newtonsoft.Json.JsonConvert.DeserializeObject(json,
new Newtonsoft.Json.JsonSerializerSettings
{
DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset
});
Console.WriteLine(jObject2.ToString());
// Result: { "Time": "2015-10-28T14:05:22.0091621+00:00" }
For me the key difference I was interested in was Speed.
I made a simple test to find out if it was faster to use JToken.Parse(string) or DeserializeObject<JToken>(string) to create a large amount of JToken and these were the results after 2,000,000 iterations using a sample of real world data
Method
Operating System Ticks
Milliseconds
JsonConvert
86123945
8612ms
JToken
67671724
6767ms
There was some variation between runs but the difference was always large.
Here is the test so you can modify it or run it yourself:
private static string s = #"{'stream':'btcusdt #bookTicker','data':{'u':20430107433,'s':'BTCUSDT','b':'21223.72000000','B':'3.29440000','a':'21223.73000000','A':'2.05450000'}}";
private static Stopwatch sw = new Stopwatch();
private static void Main(string[] args)
{
JToken convert = default;
sw.Restart();
for (int i = 0; i < 2000000; i++)
{
convert = JsonConvert.DeserializeObject<JToken>(s);
}
Console.WriteLine("JsonConvert: " + sw.ElapsedTicks + " [" + sw.ElapsedMilliseconds + "ms]");
convert.ToString();
convert = default;
sw.Restart();
for (int i = 0; i < 2000000; i++)
{
convert = JToken.Parse(s);
}
Console.WriteLine("JToken : " + sw.ElapsedTicks + " [" + sw.ElapsedMilliseconds + "ms]");
convert.ToString();
Console.ReadLine();
}
I knew an advantage that JsonConvert.DeserializeObject can deserialize an Array/List json text directly, but JObject cannot.
Try below sample code:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
namespace NetCoreJsonNETDemo
{
internal class Person
{
[JsonProperty]
internal string Name
{
get;
set;
}
[JsonProperty]
internal int? Age
{
get;
set;
}
}
internal class PersonContainer
{
public List<Person> Persons
{
get;
set;
}
}
class Program
{
static T RecoverPersonsWithJsonConvert<T>(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
static T RecoverPersonsWithJObejct<T>(string json) where T : class
{
try
{
return JObject.Parse(json).ToObject<T>();
}
catch (Exception ex)
{
Console.WriteLine("JObject threw an Exception: " + ex.Message);
return null;
}
}
static void Main(string[] args)
{
List<Person> persons = new List<Person>();
persons.Add(new Person()
{
Name = "Jack",
Age = 18
});
persons.Add(new Person()
{
Name = "Sam",
Age = null
});
persons.Add(new Person()
{
Name = "Bob",
Age = 36
});
string json = JsonConvert.SerializeObject(persons, new JsonSerializerSettings()
{
Formatting = Formatting.Indented
});
List<Person> newPersons = RecoverPersonsWithJsonConvert<List<Person>>(json);
newPersons = RecoverPersonsWithJObejct<List<Person>>(json);//JObject will throw an error, since the json text is an array.
PersonContainer personContainer = new PersonContainer()
{
Persons = persons
};
json = JsonConvert.SerializeObject(personContainer, new JsonSerializerSettings()
{
Formatting = Formatting.Indented
});
newPersons = RecoverPersonsWithJObejct<PersonContainer>(json).Persons;
newPersons = null;
newPersons = RecoverPersonsWithJsonConvert<PersonContainer>(json).Persons;
Console.WriteLine("Press any key to end...");
Console.ReadKey();
}
}
}
Apart from the answers provided here around usage, which are correct as per me :
Jobject.Parse -> when the Json is not strongly Typed or you do not know the structure of Json ahead of time
JsonConvert.DeserializeObject<T> -> When you know which class or type to cast the Json in. T can be a complex class or a simple type
My answer is based on the performance in case where the structure is not known, as given in the OP code, if we benchmark the usage of both methods for performance, it is observed that Jobject.Parse() fares well in terms of allocated memory, please ignore the name of methods, I am first calling the method with 'JsonConvert.DeserializeObject' and then second method is with Jobject.Parse

Categories

Resources