Having issues parsing a JSON response with separate columns and rows - c#

I am having issues figuring out how to parse a JSON response that I am getting back from a 3rd party. I need to convert from the following JSON (a simplified example of what I am getting back) to a C# class. The main issue is that the columns may be in a different order, have extra fields, or missing ones (Which should just end up as null values).
{
"columns": [
{ "id": { "type": "Numeric", "nullable": false } },
{ "name": { "type": "Text", "nullable": false } },
{ "description": { "type": "Text", "nullable": true } },
{ "last_updated": { "type": "DateTime", "nullable": false } }
],
"rows": [
[1, "foo", "Lorem ipsum", "2016-10-26T00:09:14Z"],
[4, "bar", null, "2013-07-01T13:04:24Z"]
]
}
The C# class for this example would be
public class Record
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime? last_updated { get; set; }
}
I have tried using a custom json converter, but did not have much luck getting it to work with the separated metadata and values. Does anyone have any ideas on how to go about parsing this kind of data? Eventually there will be multiple "Record" types, which is why the response from the server can be dynamic.

Your question is similar to this recent one and can be solved using a similar converter:
public class RowColumnListConverter<T> : JsonConverter
{
const string columnsKey = "columns";
const string rowsKey = "rows";
public override bool CanConvert(Type objectType)
{
if (!typeof(ICollection<T>).IsAssignableFrom(objectType))
return false;
// This converter is only implemented for read/write collections. So no arrays.
if (objectType.IsArray)
return false;
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var list = existingValue as ICollection<T> ?? (ICollection<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
var root = JObject.Load(reader);
var rows = root.GetValue(rowsKey, StringComparison.OrdinalIgnoreCase).NullCast<JArray>();
if (rows == null)
return list;
var columns = root.GetValue(columnsKey, StringComparison.OrdinalIgnoreCase).NullCast<JArray>();
if (columns == null)
throw new JsonSerializationException(columnsKey + " not found.");
var columnNames = columns.Cast<JObject>().SelectMany(o => o.Properties()).Select(p => p.Name).ToArray();
foreach (var row in rows)
{
if (row == null || row.Type == JTokenType.Null)
list.Add(default(T));
else if (row.Type == JTokenType.Array)
{
var o = new JObject(columnNames.Zip(row, (c, r) => new JProperty(c, r)));
list.Add(o.ToObject<T>(serializer));
}
else
throw new JsonSerializationException(string.Format("Row was not an array: {0}", row));
}
return list;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class JsonExtensions
{
public static TJTOken NullCast<TJTOken>(this JToken token) where TJTOken : JToken
{
if (token == null || token.Type == JTokenType.Null)
return null;
var typedToken = token as TJTOken;
if (typedToken == null)
throw new JsonSerializationException(string.Format("Token {0} was not of type {1}", token.ToString(Formatting.None), typeof(TJTOken)));
return typedToken;
}
}
Then use it like:
var list = JsonConvert.DeserializeObject<List<Record>>(json, new RowColumnListConverter<Record>());
Or
var list = JsonConvert.DeserializeObject<List<Record>>(json, new JsonSerializerSettings
{
Converters = { new RowColumnListConverter<Record>() },
});
The converter works by loading the outer object into a temporary JObject, then reformatting the "columns" and "rows" arrays into a more conventional list of objects for deserialization. Note that no attempt is made to use the type information in the "columns" list, it is simply assumed that the POCO members have the correct type. Also, WriteJson() is not implemented since there isn't enough information in the question to determine how to emit the type information for any possible column type; a full specification would be required.
Sample fiddle.

i think this approach is tricky somehow, but if you have to follow this, you can put order property for them to solve your order problem:
{
"columns": [
{ "id": { "type": "Numeric", "nullable": false, "order":1 } },
{ "name": { "type": "Text", "nullable": false, "order":2 } },
{ "description": { "type": "Text", "nullable": true, "order":3 } },
{ "last_updated": { "type": "DateTime", "nullable": false, "order":4 } }
],
"rows": [
[1, "foo", "Lorem ipsum", "2016-10-26T00:09:14Z"],
[4, "bar", null, "2013-07-01T13:04:24Z"]
]
}
also for null value you can get a default value to recognize them and replace with null in your custom json converter.
the best structure for your data can be:
public class Column
{
public string type { get; set; }
public bool nullable { get; set; }
public int order { get; set; }
}
public class Model
{
public List<Dictionary<string, Column>> columns { get; set; }
public List<List<string>> rows { get; set; }
}
you can convert your json to the a Model class directly.

Related

JSON.net Deserialize discrete element into list

I have the following JSON which is meant to define a form, but each "Section" (e.g. Training, Education), is not in an array.
What I need is for each of the top sections (e.g. Training, etc.) to be converted to a C# Section class, and the fields under that a List<Field>, etc. so that it can then be rendered.
Here is the JSON I am working with. I have tried various ways of deserialising but none work.
{
"readonly": "True",
"Training": {
"entity": "PNLCAND",
"type": "standard",
"mode": "update",
"fields": {
"HEADER_3": {
"caption": "Please enter all relevant work related courses",
"type": "header",
"update": "",
"mandatory": false,
"tooltip": ""
},
"HEADER_5": {
"caption": "",
"type": "header",
"update": "",
"mandatory": false,
"tooltip": ""
},
"HEADER_6": {
"caption": "",
"type": "header",
"update": "",
"mandatory": false,
"tooltip": ""
}
}
},
"Education": {
"entity": "PNLCANQUA",
"type": "repeating",
"mode": "update",
"fields": {
"Q_SUBJECT": {
"caption": "Qualifications gained",
"type": "string",
"length": 40,
"update": "Y",
"mandatory": false,
"tooltip": ""
},
"TO_DATE": {
"caption": "Date Awarded/Expected",
"type": "date",
"range": {
"start": -60,
"end": 0
},
"update": "Y",
"mandatory": false,
"tooltip": ""
},
"Q_GRADE": {
"caption": "Grade/Level",
"type": "string",
"length": 40,
"update": "Y",
"mandatory": false,
"tooltip": ""
},
"Q_LOCATION": {
"caption": "School / Colleges, Universities or Institutes of Further Education",
"type": "string",
"length": 40,
"update": "Y",
"mandatory": false,
"tooltip": ""
}
},
"values": [
{
"TO_DATE": "2019-08-01",
"Q_SUBJECT": "Qual 1",
"Q_LOCATION": "School",
"Q_GRADE": "a",
"UDF_6_EDU001": "C"
},
{
"TO_DATE": "2019-08-01",
"Q_SUBJECT": "Qual 2",
"Q_LOCATION": "School",
"Q_GRADE": "a",
"UDF_6_EDU001": "C"
},
{
"TO_DATE": "2019-08-31",
"Q_SUBJECT": "Qual 3",
"Q_LOCATION": "Uni",
"UDF_6_EDU001": "U"
}
]
},
}
You will need to use a custom JsonConverter if you want to deserialize the top-level sections and the variably-named fields within them into lists. It is possible to write the converter in a generic way which will handle both the sections and the fields.
First we need to define a model to capture the data:
public class Section
{
public string Name { get; set; }
public string Entity { get; set; }
public string Type { get; set; }
public string Mode { get; set; }
public List<Field> Fields { get; set; }
public List<Dictionary<string, string>> Values { get; set; }
}
public class Field
{
public string Name { get; set; }
public string Caption { get; set; }
public string Type { get; set; }
public Range Range { get; set; }
public int Length { get; set; }
public string Update { get; set; }
public bool Mandatory { get; set; }
}
public class Range
{
public int Start { get; set; }
public int End { get; set; }
}
Here is a generic JsonConverter which will handle creating the lists. The T generic parameter on the class specifies the item type to use for the list (e.g. Section or Field). The keyPropertyName constructor parameter tells the converter which property on the model should be used to store the item's key from the JSON. I added Name properties to both Section and Field for this purpose, so "Name" should be passed in both cases.
The converter works by loading the JSON into a JObject and iterating over its properties, excluding any whose values are not objects (so this will skip over the "readonly" property at the top level, which is obviously not a section). The remaining property values are converted to model objects using the ToObject<T> method (after first setting the name on it) and added to the return list.
public class ObjectToListConverter<T> : JsonConverter
{
public string KeyPropertyName { get; set; }
public ObjectToListConverter(string keyPropertyName)
{
KeyPropertyName = keyPropertyName;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(List<T>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = JObject.Load(reader);
List<T> list = new List<T>();
foreach (JProperty prop in obj.Properties().Where(p => p.Value.Type == JTokenType.Object))
{
JToken item = prop.Value;
item[KeyPropertyName] = prop.Name;
list.Add(item.ToObject<T>(serializer));
}
return list;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use this converter, pass an instance for both Section and Field to JsonConvert.DeserializeObject like this:
List<Section> sections = JsonConvert.DeserializeObject<List<Section>>(json,
new ObjectToListConverter<Section>("Name"), new ObjectToListConverter<Field>("Name"));
Here is a working demo: https://dotnetfiddle.net/A4uAGk
You have to create a Training class it looks exactly how your json structure. Then use the dapper/automapper to bind the section model object.

Generate JSON with multiple nodes with the same name

I need to create objects in c# that get converted to the following JSON:
{
"Order": {
"CustomerCode": "9999999",
"Note": "New Order for Test -- UNIT TEST",
"Stops": {
"Stop": {
"Sequence": "1",
"StopType": "P",
"Name": "CVS"
},
"Stop": {
"OrderStopID": "5",
"Sequence": "2",
"StopType": "D",
}
},
"Jobs": {
"Job": {
"Sequence": "1",
"Drivers": {
"Driver": {
"Sequence": "1",
"DriverCode": "09"
},
"Driver": {
"Sequence": "2"
}
}
}
}
}
}
Here are the objects I created to represt this:
public class RootObject
{
public Order Order { get; set; }
}
public class Order
{
public string CustomerCode { get; set; }
public List<Stop> Stops { get; set; }
public List<Job> Jobs { get; set; }
}
When I use JSON.NET to serialize the root object I get the following:
Notice taht hte Stops and Jobs are generated as an array, how do I make it so it gets genrated as the JSON is showed at the beginning?
{
"Order": {
"CustomerCode": "9999999",
"Stops": [
{
"Sequence": "1",
"StopType": "P",
"Name": "CVS"
},
{
"OrderStopID": "5",
"Sequence": "2",
"StopType": "D",
}
],
"Jobs": [
{
"Sequence": "1",
"Drivers": [
{
"Sequence": "1",
"DriverCode": "09"
},
{
"Sequence": "2"
}
]
}
]
}
}
You can use a JsonConverter for this:
public class ArrayConverter : JsonConverter
{
private readonly string _propertyName;
public ArrayConverter(string propertyName)
{
this._propertyName = propertyName;
}
public override bool CanConvert(Type objectType)
{
return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(List<>);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
IList items = (IList)Activator.CreateInstance(objectType);
var modelType = objectType.IsArray ? objectType.GetElementType() : objectType.GetGenericArguments().Single();
if (reader.TokenType != JsonToken.StartObject)
{
throw new ArgumentOutOfRangeException(nameof(reader), "Expected object.");
}
while (reader.Read() && reader.TokenType != JsonToken.EndObject)
{
reader.Read();
items.Add(serializer.Deserialize(reader, modelType));
}
return items;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is IEnumerable enumerableValue)
{
JObject obj = new JObject();
writer.WriteStartObject();
foreach (var val in enumerableValue)
{
writer.WritePropertyName(_propertyName);
serializer.Serialize(writer, val);
}
writer.WriteEndObject();
}
else
{
throw new ArgumentOutOfRangeException(nameof(value), "Value does not implement IEnumerable.");
}
}
}
Try it online

How to deserialize JSON with Json.Net and Xamarin with SubTypes?

I'm trying to deserialize this json:
{
"teaser": [{
"id": "...",
"type": "category",
"url": "https:...",
},{
"id": "...",
"type": "brand",
"url": "https:...",
"videoCount": 1,
},{
"id": "...",
"type": "video",
"url": "https:...",
"headline": "...",
}]
}
It has a list of teasers whereby each teaser is different depending on its type.
These would be my objects:
public class StartPage
{
public IList<Teaser> Teaser { get; set; }
}
public abstract class Teaser
{
public string Id { get; set; }
public string Url { get; set; }
}
public class Video : Teaser
{
public string Headline { get; set; }
}
public class Brand : Teaser
{
public int VideoCount { get; set; }
}
I am new to Json.NET and Xamarin and couldn't find a solution for this case yet. Before, when I was using Android Studio and Gson, I could register sybtypes the following way:
RuntimeTypeAdapterFactory<Teaser> teaserRuntimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(
Teaser.class, "type")
.registerSubtype(Video.class, Teaser.TYPE_VIDEO)
.registerSubtype(Brand.class, Teaser.TYPE_BRAND)
.registerSubtype(Category.class, Teaser.TYPE_CATEGORY);
return new GsonBuilder()
.registerTypeAdapterFactory(teaserRuntimeTypeAdapterFactory);
Is there a similar way to achieve what I want with Json.NET I have overlooked yet?
What you can do is create custom JsonConverter please below snippet and find this dot net fiddle
string json ="{ 'Teaser': [{ 'id': '...', 'type': 'category', 'url': 'https:...', },{ 'id': '...', 'type': 'brand', 'url': 'https:...', 'videoCount': 1, },{ 'id': '...', 'type': 'video', 'url': 'https:...', 'headline': '...', }]}";
var list = JsonConvert.DeserializeObject<StartPage>(json);
public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver
{
protected override JsonConverter ResolveContractConverter(Type objectType)
{
if (typeof(Teaser).IsAssignableFrom(objectType) && !objectType.IsAbstract)
return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow)
return base.ResolveContractConverter(objectType);
}
}
public class BaseConverter : JsonConverter
{
static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() };
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Teaser));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
switch (jo["type"].Value<string>())
{
case "video":
return JsonConvert.DeserializeObject<Video>(jo.ToString(), SpecifiedSubclassConversion);
case "brand":
return JsonConvert.DeserializeObject<Brand>(jo.ToString(), SpecifiedSubclassConversion);
default:
throw new Exception();
}
throw new NotImplementedException();
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException(); // won't be called because CanWrite returns false
}
}
Feel free to create custom logic in switch case hard code class name might not good idea instead create enum or something like but this is how you can achieve this type of scenario

JSON .Net not respecting PreserveReferencesHandling on Deserialization

I have doubly linked list that I am trying to deserialise.
My scenario closely relates to this SO: Doubly Linked List to JSON
I have the following JSON settings:
_jsonSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ObjectCreationHandling = ObjectCreationHandling.Auto
};
When I look at the serialised output, it appears correct, and the references between nodes are properly represented.
When the data is deserialised, the Parent properties in the Child objects are null, even though they are populated with $ref correctly.
Below is a sample of the JSON (trimmed for readability)
In the process of typing this question - I may have seen the source of the trouble...
The objects in the "Children" array property do not have $type attributes.
This could be because the Children and Parent properties are of generic type T.
Note that the actual type being serialised is a derived class of TemplateDataLinkedListBase
public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement>
Here is an excerpt of the base class:
public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T>
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public T Parent { get; set; }
[JsonProperty(TypeNameHandling=TypeNameHandling.Objects)]
public List<T> Children { get; set; }
}
How can I deserialise this JSON in such a way that the Parent property is not null and contains a reference to the parent object?
{
"$id": "9",
"$type": "Contracts.Models.TemplateDataQueryElement, Contracts",
"Query": null,
"Parent": null,
"Children": [
{
"$id": "11",
"Query": null,
"Parent": {
"$ref": "9"
},
"Children": [
{
"$id": "13",
"Query": null,
"Parent": {
"$ref": "11"
},
"Children": [],
"EntityName": "Widgets",
"Fields": [
"Id"
],
"Key": ""
},
Here are PasteBin links to the relevant code:
http://pastebin.com/i1jxVGG3
http://pastebin.com/T1xqEWW2
http://pastebin.com/ha42SeF7
http://pastebin.com/cezwZqx6
http://pastebin.com/uFbTbUZe
http://pastebin.com/sRhNQgzh
Here is what I tried and worked fine:
The classes
public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T>
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public T Parent { get; set; }
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public List<T> Children { get; set; }
}
public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement>
{
public string Query { get; set; }
public TemplateDataQueryElement()
{
Children = new List<TemplateDataQueryElement>();
}
}
Initialization
var childLowest = new TemplateDataQueryElement
{
Query = "Lowest"
};
var childMiddle = new TemplateDataQueryElement
{
Query = "Middle",
Children = new List<TemplateDataQueryElement>
{
childLowest
}
};
childLowest.Parent = childMiddle;
var parent = new TemplateDataQueryElement
{
Query = "Parent",
Children = new List<TemplateDataQueryElement>
{
childMiddle
}
};
childMiddle.Parent = parent;
Serialization settings
var _jsonSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ObjectCreationHandling = ObjectCreationHandling.Auto
};
Serialization
var serializedStr = JsonConvert.SerializeObject(parent, Formatting.Indented, _jsonSettings);
The serialized json looks like this:
{
"$id": "1",
"Query": "Parent",
"Parent": null,
"Children": [
{
"$id": "2",
"Query": "Middle",
"Parent": {
"$ref": "1"
},
"Children": [
{
"$id": "3",
"Query": "Lowest",
"Parent": {
"$ref": "2"
},
"Children": []
}
]
}
]
}
Deserialization
var deserializedStructure = JsonConvert.DeserializeObject<TemplateDataQueryElement>(serializedStr, _jsonSettings);
The references in the deserializedStructure are preserved correctly.
Demo
https://dotnetfiddle.net/j1Qhu6
UPDATE 1
The reason my example works, and the code you posted in the additional links doesn't is because my classes contain default constructor, and yours don't. Analyzing your classes, adding a default constructor to them, it won't break the functionality and the deserialization will be successful with Parent property initialized correctly. So what you basically need to do is add a default constructor to both classes:
public class TemplateDataLinkedListBase<T> where T : TemplateDataLinkedListBase<T>
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public T Parent { get; set; }
[JsonProperty(TypeNameHandling=TypeNameHandling.Objects)]
public List<T> Children { get; set; }
public string EntityName { get; set; }
public HashSet<string> Fields { get; set; }
public string Key { get { return getKey(); } }
public TemplateDataLinkedListBase()
{
Children = new List<T>();
Fields = new HashSet<string>();
}
public TemplateDataLinkedListBase(string entityName)
{
EntityName = entityName;
Children = new List<T>();
Fields = new HashSet<string>();
}
private string getKey()
{
List<string> keys = new List<string>();
keys.Add(this.EntityName);
getParentKeys(ref keys, this);
keys.Reverse();
return string.Join(".", keys);
}
private void getParentKeys(ref List<string> keys, TemplateDataLinkedListBase<T> element)
{
if (element.Parent != null)
{
keys.Add(element.Parent.EntityName);
getParentKeys(ref keys, element.Parent);
}
}
public T AddChild(T child)
{
child.Parent = (T)this;
Children.Add(child);
return (T)this;
}
public T AddChildren(List<T> children)
{
foreach (var child in children)
{
child.Parent = (T)this;
}
Children.AddRange(children);
return (T)this;
}
public void AddFields(IEnumerable<string> fields)
{
foreach (var field in fields)
this.Fields.Add(field);
}
public TemplateDataLinkedListBase<T> Find(string searchkey)
{
if (this.Key == searchkey)
{
return this;
}
else
{
foreach (var child in Children)
{
if (child.Key == searchkey)
{
return child;
}
else
{
var childResult = child.Find(searchkey);
if (childResult != null) return childResult;
}
}
}
return null;
}
}
public class TemplateDataQueryElement : TemplateDataLinkedListBase<TemplateDataQueryElement>, ITemplateDataQueryElement
{
public string TemplateModelName { get; set; }
public string RecordId { get; set; }
public string ParentForeignKeyName { get; set; }
public string Query { get; set; }
public dynamic ObjectData { get; set; }
public ITemplateDataParseResult ParseResult { get; set; }
public TemplateDataQueryElement() : base()
{
Fields.Add("Id"); //Always retrieve Id's
ObjectData = new ExpandoObject();
}
public TemplateDataQueryElement(string entityName)
: base(entityName)
{
Fields.Add("Id"); //Always retrieve Id's
ObjectData = new ExpandoObject();
}
public override string ToString()
{
return string.Format("{0}: {1}", EntityName, Query);
}
}
The EntityName property which you set through your constructor, will be deserialized correctly, since it is a public property.

How to check if a JSON output returns 1 or more rows so that either a string or arraylist can be used and mapped accordingly to an object in C#

How can I check if a JSON output returned just 1 row of an element or multiple rows.
For example, I have the below JSON output.
{
"orders": {
"order": [
{
"id": 100,
"type": "market",
"symbol": "AAPL",
"side": "buy"
}]
}
}
I have the below code for deserializing the JSON output:
dynamic dynObj = JsonConvert.DeserializeObject(response);
Console.WriteLine("ID: {0}\n Type: {1}\n Symbol: {2}\n Side: {3}\n", dynObj.orders.order.id, dynObj.orders.order.type,dynObj.orders.order.symbol,dynObj.orders.order.side;
But, most of the times I get the output as an array of order object as below
{
"orders": {
"order": [
{
"id": 101,
"type": "market",
"symbol": "AAPL",
"side": "buy"
},
{
"id": 102,
"type": "market",
"symbol": "MSFT",
"side": "buy"
},
{
"id": 103,
"type": "limit",
"symbol": "AMZN",
"side": "buy"
}
]
}
}
So I deserialize the above Json output as shown:
dynamic dynObj = JsonConvert.DeserializeObject(response);
foreach (var c in dynObj.orders.order)
{
Console.WriteLine("Id : {0}\n", c.id);
Console.WriteLine("Type : {0}\n", c.type);
Console.WriteLine("Symbol : {0}\n", c.symbol);
Console.WriteLine("Side : {0}\n", c.side);
}
I have two important questions:
How can I know whether the JSON returned is only 1 row of Order
object or a collection so that I can use the relevant way to loop.
If it is an array, do I need to deserialize as below and if so, how
do I add them to Order Class/Object?
RootObject ord = JsonConvert.DeserializeObject(response);
Any help is greatly appreciated.
I would highly recommend you stop using dynamic because I think this is an abuse of the type. Instead I would define types to deserialize into. By doing so you give yourself type safety and working with the data becomes trivial. Below are class definitions an example using the generic deserialize method (Deserialize<T>(string input)).
public class order
{
public int id { get; set; }
public string type { get; set; }
public string symbol { get; set; }
public string side { get; set; }
}
public class orders
{
List<order> order { get; set; }
}
JsonSerializer serializer = new JsonSerializer();
orders myOrders = serializer.Deserialize<orders>(response);
if (myOrders.order.Count() == 1)
// we have 1 order
Able to fix the issue pertaining to check if the JSON result returned is an object or an array.
Modified my RootObject which has the Array/Object as follows:
[JsonConverter(typeof(GenericListCreationJsonConverter<T>))]
public List<T> order { get; set; }
Added the below class which checks for JSON
internal class GenericListCreationJsonConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<List<T>>(reader);
}
else
{
T t = serializer.Deserialize<T>(reader);
return new List<T>(new[] { t });
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

Categories

Resources