JSON.NET "System.OutOfMemoryException" when deserializing into a DataSet - c#

Very new to C# and JSON. I have been struggling with this problem for about a day and can't figure it out.
JSONLint states both JSON strings are valid.
Trying to deserialize the following
{"items":[{"id":"1122267","quantity":"1","bundle":"1"}],"seconds_ago":"1"}
throws the exception
An unhandled exception of type 'System.OutOfMemoryException' occurred
in Newtonsoft.Json.dll
If I try
{"items":[{"id":"1122267","quantity":"1","bundle":"1"}]}
it works.
I'm reading the JSON string from a textbox and then deserializing using the following
string json = textBox1.Text;
DataSet dataSet = JsonConvert.DeserializeObject<DataSet>(json);

I'm not at all familiar with DataSet, but after playing around with it a bit, I think I've found the reason. According to the documentation:
The DataSet consists of a collection of DataTable objects
Because a DataSet is a collection of objects, you can only deserialize a collection (array) into it. So,
{"items":[{"id":"1122267","quantity":"1","bundle":"1"}],"seconds_ago":"1"}
will not work, because it contains one collection (of items) and one property (seconds_ago). On the other hand,
{"items":[{"id":"1122267","quantity":"1","bundle":"1"}]}
works, because it only contains one collection.
Therefore, if you really want to deserialize your json string into a DataSet, you should do this instead:
{
"objects": [
{
"items":[{"id":"1122267","quantity":"1","bundle":"1"}],
"seconds_ago":"1"
}
]
}
You should really consider deserializing into a C# object though, which in my opinion is less complicated and easier to handle:
public class RootObject
{
public List<Item> items { get; set; }
public string seconds_ago { get; set; }
}
public class Item
{
public string id { get; set; }
public string quantity { get; set; }
public string bundle { get; set; }
}
RootObject deserializedObject = JsonConvert.DeserializeObject<RootObject>(json);

The DataSet class does not have a property seconds_ago or items. So therefore
DataSet dataSet = JsonConvert.DeserializeObject<DataSet>(json);
Will never work because you cannot convert that particular json string to a DataSet

Related

Deserializing an unnamed array

I am having difficulty writing the appropriate annotations to represent data which is returned from a JSON Get request which returns data like so:
[{"ProductCode":"0129923083091","Description":"DIESEL ","SalesLitres":6058.7347,"SalesValue":6416.2000},{"ProductCode":"0134039344902","Description":"UNLEADED ","SalesLitres":3489.8111,"SalesValue":3695.7100},
...
]
(ellipsis above just indicating that I could have variable number of these items returned)
In my model class (I am using MVVM approach for a Xamarin project but that's not over relevant here) I am using annotations to represent the model attributes
namespace App8.Models
{
public class ReportRow
{
[JsonProperty("ProductCode")]
public string ProductCode { get; set; } = string.Empty;
[JsonProperty("Description")]
public string Description { get; set; } = string.Empty;
[JsonProperty("SalesLitres")]
public double SalesLitres { get; set; } = 0.0;
[JsonProperty("SalesValue")]
public double SalesValue { get; set; } = 0.0;
}
}
I would like to annote another class which shows the container/contained relationship. However, I coming unstuck as there is no JSON attribute to provide in the annotation to represent the "root" of the returned collection.
I'd have no problem mapping the JSON to an object model for any JSON arrays which are named within the returned JSON. In that case I could create another class with a named JSON attribute which contained a C# List but I am trying to provide an appropriate model mapping for JSON which returns a list of items within an unnamed array.
Any ideas how I could approach this?
To deserialize that JSON, use:
JsonConvert.DeserializeObject<List<ReportRow>>(json)
(or any variant you wish, the key here is asking to deserialize a ICollection of ReportRow. It could be your own class implementing ICollection, or any of the builtins)
The same idea follows to JsonTextReader or whatever other means of deserializing JSON.NET offers. Just use a ICollection<YourType> as target type.

Separating array and element from a JSON string

I am connecting you Google Places API to retrive results in the form of a JSON string. You can view the complete format of the string Here.
If you a look at it you will see that the actual results array starts after two elements which are html_attributions and next_page_token.
So When i try to deserialize it in this way:
var serializer = new JavaScriptSerializer();
var arr= serializer.Deserialize(result,typeof(string[]));
I get an empty array.
My question is how is there a way i can separate html_attributions and next_page_token fields and the pass the valid results array from the string to be deserialized?
I don't understand the part where you wish to seperate the html_attributions and the next_page_token.
Wouldn't it be sufficient to just deserialize the response with whatever properties that you need?
For example, you can deserialize the response to only retrieve the values that you desire;
// I represent the wrapper result
class Result
{
public List<string> html_attributions { get; set; }
public string next_page_token { get; set; }
public List<ResultItem> results { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
}
// the actual deserialization
Result Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
return serializer.Deserialize(json, typeof(Result));
}
Edit:
The reason that your deserialization doesn't return you a array of strings is because the response that you retrieve is infact an object and not an array, however the parameter within that object which is named results is an array. In order for you to deserialize more properties you'll have to define them in your "ResultItem" class, sorry for my poor naming here.
For instance, if you'd wish to also retrieve the icon property per result you'll have to add a property named "icon" of type string.
Meanwhile the property "photos" is an array, in order to deserialize it you'll have to create another class and add a property of type list/array of that newly created class, and it has to be named "photos" unless you use a different serializer or use DataContract and DataMember attributes (using the Name property for field mapping).
// the representation of a photo within a result item
class Photo
{
public int height { get; set; }
public List<string> html_attributions { get; set; }
public string photo_reference { get; set; }
public int width { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
// the added icon
public string icon { get; set; }
// the added photos collection, could also be an array
public List<Photo> photos { get; set; }
}
Just look at the JSON result to figure out what other properties that you might want to add, for instance the "scope" property is an string whilst the "price_level" is an integer.
If I understand your comment correctly you're only interested in the actual results, you'll still have to deserialize the response correctly with its wrapper.
// the actual deserialization
List<ResultItem> Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize(json, typeof(Result));
return result.results;
}
Edit2:
If you really want a string[] as a result you could simply take use of System.Linq using the code above.
string[] stringArray = result.results.Select(r => string.Format("id:{0} - name:{1}", r.id, r.name)).ToArray();
Edit3:
Instead of using the JavascriptSerializer you could use JObject functionality which can be found in the Newtonsoft.Json.Linq library.
var jsonObject = JObject.Parse(json);
string[] results = jsonObject.SelectTokens("results").Select(r => r.ToString()).ToArray();
This will give you an array of strings where each value within the array is the actual json string for each result.
If you however would like to query for the coordinates only:
var jsonObject = JObject.Parse(json);
var coordinates = jsonObject["results"]
.Select(x => x.SelectToken("geometry").SelectToken("location"))
.Select(x => string.Format("{0},{1}", (string)x.SelectToken("lat"), (string)x.SelectToken("lng")))
.ToArray();
This would give you an array of coordinates, eg:
[
"-33.867217,151.195939",
"-33.866786,151.195633",
...
]
Whatever approach you choose you'll be able to accomplish same results using either Newtonsoft or the .net serializer, whilst the Newtonsoft approach would allow you to query without creating strong types for deserialization.
I don't find the point of "[...] pass the valid results array from the string to be deserialized".
Maybe you need to switch to JSON.NET and do something like this:
// You simply deserialize the entire response to an ExpandoObject
// so you don't need a concrete type to deserialize the whole response...
dynamic responseEntity = JsonConvert.DeserializeObject<ExpandoObject>(
googlePlacesJson, new ExpandoObjectConverter()
);
// Now you can access result array as an `IEnumerable<dynamic>`...
IEnumerable<dynamic> results = responseEntity.results;
foreach(dynamic result in results)
{
// Do stuff for each result in the whole response...
}

Deserializing a json object with nested array and displaying in a DataSet

Hi I am having trouble deserializing my JSON object and displaying in a DataSet.
I have had partial success in that I am displaying most of the data except for the data that is contained in nested arrays.
What I have done is deserialize the data into a generic list and then from that list have converted it into a dataset which I will insert into a database. I am using JSON.net, more specifically the JsonConvert class to deserialize the string and then using a helper class to convert it to a dataset, which I stole from here and modified From Here. Now this works well until I hit the nested arrays.
On the first array I can see the data being returned in the XML but it does not appear in the returned dataset and the other array simply does not feature in the request at all.
I am handling the first array as below:
public List<string> Categories { get; set; }
This is what was as per json2csharp and it partially works in that I can see the categories options change depending on how many I select. For instance if i select 4 it will return 4 but the rows will be empty.
After I click on Categories it displays these 4 rows.
Any ideas on how i can get around this?
My second array is not being requested or returned at all. I have created a seperate class to handle the elements as below:
public class EventDate
{
public DateTime EndDate { get; set; }
public DateTime StartDate { get; set; }
}
and call it using this:
public List EventDates { get; set; }
This produces the error Column requires a valid Data Type
I have seen some examples that say you have to deserialize these using custom converters and others that say you should be able to do as I have done above. The format for the above are as per the json2charp website.
I have tried accessing the Categories as a string array but the same issue arose where I could see the returned data in the XML but not in the dataset.
Below I have included the deserializejson class that uses the Jsonconvert class to deserialize the jsonstring.
public List<RootObject> DeSerializeJsonString(string jsonString)
{
List<RootObject> list = new List<RootObject>();
list = (List<RootObject>)JsonConvert.DeserializeObject<List<RootObject>>(jsonString);
return list;
}
Any help would be greatly appreciated, cheers.

Discard items when deserializing Json collection with Json.net

I have a JSON stream that sends collections looking like this:
[{"channel":"24e67e0d-1cad-4cc09e7af8523ef460fe",
"op":"private",
"origin":"broadcast",
"stamp":1388680103991749},
"13886801043507640",
"24e67e0d-1cad-4cc0-9e7a-f8523ef460fe"]
The first object is no problem, but the last two ones ("13886801043507640" and "24e67e0d-1cad-4cc0-9e7a-f8523ef460fe") make Json.Net throw exceptions they don't even follow the format {field:name,field:name}.
How can I handle these objects properly (or at least discard them) using Json.Net?
Cheers, any input is appreciated.
Here is one approach to extract the object data from the JSON using Json.Net.
First define a class for the object:
class Item
{
public string channel { get; set; }
public string op { get; set; }
public string origin { get; set; }
public long stamp { get; set; }
}
Then use Json.Net's LINQ-to-JSON API to parse and filter the array, extracting only the object part:
JArray array = JArray.Parse(json);
Item item = array.Children<JObject>().First().ToObject<Item>();
If there can be multiple objects, you can get them into a list like this (assuming they all have the same structure):
List<Item> items = array.Children<JObject>()
.Select(jo => jo.ToObject<Item>())
.ToList();
Similarly, you can get the string values from the array like this:
List<string> strings = array.Children<JValue>()
.Select(jv => jv.ToString())
.ToList();

Parsing JSON data with javascriptserializer throws exception

I am getting JSON data in following format:
"[[\"NAME\",\"state\"],\n[\"Alabama\",\"01\"],\n[\"Alaska\",\"02\"]]"
I am trying to parse it with System.Web.Script.Serialization.JavaScriptSerializer the following way:
[Serializable]
private class State
{
private string Name { get; set; }
private string Code { get; set; }
}
private static List<State> getStates(string jsonData)
{
var json = new JavaScriptSerializer();
var parsedResult = json.Deserialize<List<State>>(jsonData);
return parsedResult;
}
The error I am getting is Type 'State' is not supported for deserialization of an array.
What am I doing wrong?Please suggest a way out.
Update
I have somewhat solved the problem since I need only a collection of the data in a queryable format. The following code did the trick:
var parsedResult = json.Deserialize<List<string[]>>(jsonData);
You have found workaround, nevertheless I think it's good to have explanation for the issue.
The problem you have is in brackets; [] means array, whereas object is market with {}. This means that to deserialize input string in array of State objects your input string should be formatted in following way:
[{\"NAME\",\"state\"},\n{\"Alabama\",\"01\"},\n{\"Alaska\",\"02\"}]

Categories

Resources