Parsing json with an odd format - c#

Note: Please post in comments if you have a better title for this post. Thanks!
I am working with an api that returns json that looks like the snippet below. I am pretty familiar with json but I have never seen a structure that looks like this.
The resultFieldList fieldNames are fixed (4 below but there are about 20). The values array is variable, but the same length for each fieldName.
I am thinking that I should count a non-null field (OrderNumber) and then iterate through each fieldName, but that seems inefficient. I am wondering if there is a better way. This is how I am getting the record count:
string result = await response.Content.ReadAsStringAsync();
JObject m_json = (JObject)JsonConvert.DeserializeObject(result);
int m_count = m_json["resultFieldList"][3]["values"].Count();
This is the json snippet.
{
"collectionName": "Transactions",
"recordCount": 0,
"skippedRecordCount": 0,
"resultFieldList":
[{
"fieldName": "SaleChannel",
"analyticsDataType": "STRING",
"values": ["Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online", "Online"]
},
{
"fieldName": "Quantity",
"analyticsDataType": "INTEGER",
"values": [1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 2, 2, 1, 3]
},
{
"fieldName": "Amount",
"analyticsDataType": "CURRENCY",
"values": ["25.00", "14.00", "14.00", "50.00", "14.00", "50.00", "14.00", "25.00", "18.00", "50.00", "36.00", "25.00", "50.00", "25.00", "14.00", "25.00", "50.00", "50.00", "25.00", "75.00"]
},
{
"fieldName": "OrderNumber",
"analyticsDataType": "STRING",
"values": ["60937129", "60937129", "53221966", "14599547", "14599547", "10478305", "10478305", "95344699", "95344699", "83413371", "65720270", "43458148", "52500933", "32742144", "32742144", "89850688", "61514108", "11080559", "90497730", "22838522"]
}]
}
The precise question is is it possible to create a new array or object where the index of each value is combined into a new object given this structure. For example, the values of index[0] are "Online",1,"25.00","60937129". So it may be a new array or a whole new json object. If so, how would I create either one or the other? The output would be something like this:
[ { "Online",1,"25.00","60937129" },{...} ]
Or
{"results": [ { "SaleChannel": "Online" , "Quantity": 1, "Amount": "25.00", "OrderNumber": "60937129" } , {...} ]
ANSWER
Using #JLRishe's answer I am able to parse and use the data for the use case - inserting api data into a data warehouse.

Edit: I believe I have addressed the main point of your question toward the end of my answer.
You're probably best off defining a type to represent this structure. Since the contents of the values arrays have varying types, you can't really make an assumption about them and would probably be safest treating them as objects:
public class MyClass
{
public string collectionName { get; set; }
public int recordCount { get; set; }
public int skippedRecordCount { get; set; }
public ResultField[] resultFieldList { get; set; }
}
public class ResultField
{
public string fieldName { get; set; }
public string analyticsDataType { get; set; }
public object[] values { get; set; }
}
Then parsing the JSON is simply a matter of:
string result = await response.Content.ReadAsStringAsync();
MyClass obj = JsonConvert.DeserializeObject<MyClass>(result);
When actually using the data, you would need to look at the value in analyticsDataType for each respective ResultField and cast the contents of the values properties accordingly.
Alternatively, if you don't care that much about treating the values as their original types, you could treat them all as strings, which would require less type checking. Everything else would remain the same:
public string[] values { get; set; }
Edit: Now that you've clarified the question a bit more, it seems this is mostly about arranging the data into a more nicely consumable fashion.
If there isn't anything outside the resultFieldList that indicates the number of values in each value array, then you would basically need to arbitrarily pick one to get the count:
int valueCount = obj.resultFieldList[0].values.Length;
Then once you have that, you could assemble the values together:
// assuming OrderItem is a class you've defined with the needed
// properties for one entry in an order
IEnumerable<OrderItem> items = Enumerable
.Range(0, valueCount)
.Select(i => new OrderItem {
SaleChannel = Convert.ToString(obj.resultFieldList[0].values[i]),
Quantity = Convert.ToInt32(obj.resultFieldList[1].values[i]),
Amount = Convert.ToDecimal(obj.resultFieldList[2].values[i]),
// etc...
});

Use Newtonsoft.Json and do something like this:
Define your classes:
public class SomeObject
{
public List<DynamicProperty> resultFieldList { get; set; }
}
public class DynamicProperty
{
public string fieldName { get; set; }
public string analyticsDataType { get; set; }
public List<object> values { get; set; }
}
Deserialize your JSON:
var obj = JsonConvert.DeserializeObject<SomeObject>(json);
var t = obj.resultFieldList[3].values.Count();
Please let me know if you have any questions. I tested using your JSON and the answer was 20. Additionally, you could use Linq to do more complex analysis.
Edited: Based on question being expanded.
Create an Order class:
public class Order
{
public string SaleChannel { get; set; }
public int Quantity { get; set; }
public Decimal Amount { get; set; }
public string OrderNumber { get; set; }
}
Generate the json enumerable based and serialize:
var jsonValue = JsonConvert.SerializeObject(Enumerable
.Range(0, obj.resultFieldList.Max(x => x.values.Count))
.Select(i => new Order()
{
SaleChannel = obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.SaleChannel))?.values[i].ToString(),
Quantity = Convert.ToInt32(obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.Quantity))?.values[i].ToString()),
Amount = Convert.ToDecimal(obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.Amount))?.values[i].ToString()),
OrderNumber = obj.resultFieldList.FirstOrDefault(x => x.fieldName == nameof(Order.OrderNumber))?.values[i].ToString()
}));

Related

converting enums to json objects

I have two enums:
enum myEnum
{
item_one = 1,
item_two = 2,
}
enum myEnum2
{
item_four = 4,
item_five = 5,
}
I would like to represent these as Json objects so that I can send then off when a http request is made. the goal is to have them look like:
{
myEnum:{[
{
"code": 1, "definition": "item_one"
},
{
"code": 2, "definition": "item_two"
}
]},
myEnum2:{[
{
"code": 4, "definition": "item_four"
},
{
"code": 5, "definition": "item_five"
}
]},
}
I would create an in-between mapping object, that can be put through a serializer like Newtonsoft.Json or System.Text.Json:
// Out mapping object
class EnumMapper {
public int Code { get; set; }
public string Definition { get; set; }
}
// Create the target output format
var result = new Dictionary<string, List<EnumMapper>>();
// Go over an enum and add it to the dictionary
// Should properly be made into a method so it easier to add more enums
foreach(var enumValue in Enum.GetValue(typeof(myEnum))) {
// List containing all values of a single enum
var enumValues = new List<EnumMapper>();
enumValues.Add(new EnumMapper {
Code = (int)enumValue,
Description = Enum.GetName(enumValue)
});
// Add the enum values to the output dictionary
result.Add("myEnum", enumValues)
}
// Serialize to JSON
var json = JsonConvert.Serialize(result)
I haven't tested the above code - but you should be able to grasp the general idea from it.

C# Sorting JSON response string

I'm making a HTTP request to gain a response body of JSON. The response body is quite large so I've cut the structure down for convenience. My problem is accessing specific data for later use. Then JSON string looks as follows:
{
"0": { <--- ID number
"vk": { <--- Specific object
"cost": 19, <--- information about object
"count": 1903 <--- information about object
},
"ok": {
"cost": 4,
"count": 2863
},
"wa": {
"cost": 4,
"count": 2210
}
}
}
I'm trying to define some sort of class or structure which would allow me to:
Call the ID to return all the blocks in that ID
Call ID.Object to get the cost and count
Call ID.Object.Count to return the account.
But I'm having trouble even separating the data with JSON. I have tried three methods I found on Stack Overflow to not much success, the closest I got was using a token
JToken token = JObject.Parse(response);
and calling token.root to pull the entire block & token.path to pull the ID number. I've seen suggestions about making each field have its own method but there are over 100 "id" brackets which contain upto 50 objects each so that's not really plausible.
I'm asking for assistance on how I would split the JSON data into organised data, I am planning to just create a class to store the data unless theres some specific JSON storage aspect I'm not aware of.
One way to code it would be to Parse the input and Deserialize the "0" to a class.
var obj = JObject.Parse(jsonDoc);
var something = JsonConvert.DeserializeObject<AllObjects>(obj["0"].ToString());
and your classes would look like this (I know you can name them better :) )
public class ObjInfo
{
public int cost { get; set; }
public int count { get; set; }
}
public class AllObjects
{
public ObjInfo vk { get; set; }
public ObjInfo ok { get; set; }
public ObjInfo wa { get; set; }
}
Reason you might have to do the way i did above is because you cannot have a variable with number... like public AllObjects 0 {get;set;}, but, you CAN do the following
public class MainObj
{
[JsonProperty("0")]
public AllObjects Zero { get; set; }
}
using the following line would deserialize correctly.
var something2 = JsonConvert.DeserializeObject<MainObj>(jsonDoc);
// where jsonDoc is your actual string input.
EDIT:
If your initial json will have a random ID (not a 0), you can use the following code to look up that ID. Then you can query your objects to see which one needs updating.
var obj = JObject.Parse(jsonDoc);
var zeroethElement = ((JProperty)obj.Properties().First()).Name;
I wanted to post an alternative solution where you can serialize the 0 index and any other indexes that follow it to achieve something like this.
The trick is to use a Dictionary. If you expect that the ID number will always be an integer, then you can construct the first part of the dictionary like this to start with.
Dictionary<int, ...>
And if it's a string, just change the int to a string.
If VK, OK and WA are the only 3 elements you expect, you can use the AllObjects class from Jawads answer like this.
// Dictionary<int, AllObjects>>
JsonConvert.DeserializeObject<Dictionary<int, AllObjects>>(json);
I would also modify Jawads AllObjects class to make sure the property names comply with C# conventions by using the JsonProperty attributes to our advantage like this.
public class AllObjects
{
[JsonProperty("vk")]
public CostCountResponse Vk { get; set; }
[JsonProperty("ok")]
public CostCountResponse Ok { get; set; }
[JsonProperty("wa")]
public CostCountResponse Wa { get; set; }
}
The output of deserializing will give us this result.
If however you are expecting more elements than just VK, OK and WA, you can cover this case with another nested dictionary like this.
Dictionary<int, Dictionary<string, ...>>
This string in the nest Dictionary is what will contain vk, ok, etc..
So what we have now is a Dictionary within a Dictionary which accurately represents how the JSON data is nested so far.
The final part is deserializing the JSON element containing the Cost and Count properties, and we can use the class Jawad posted to do that (I'm showing one that's again slightly modified to keep with naming conventions)
public class ObjInfo
{
[JsonProperty("cost")]
public int Cost { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
We can now use the ObjInfo class as the final puzzle of the Dictionary we've been defining.
Dictionary<int, Dictionary<string, ObjInfo>>
Which we can use like this (I've included the modified JSON I've been using as well to demonstrate what we can do here)
static void Main()
{
var root = JsonConvert.DeserializeObject<Dictionary<int, Dictionary<string, ObjInfo>>>(testJson);
foreach (var item in root)
{
Console.WriteLine($"Id: {item.Key}");
foreach (var subitem in item.Value)
{
Console.WriteLine($" SubCode: {subitem.Key}");
Console.WriteLine($" Cost: {subitem.Value.Cost}");
Console.WriteLine($" Count: {subitem.Value.Count}\n");
}
}
// Or access individual items by
var zeroVk = root[0]["vk"];
// Console.WriteLine(zeroVk.Cost);
// Console.WriteLine(zeroVk.Count);
}
public class ObjInfo
{
[JsonProperty("cost")]
public int Cost { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
const string testJson = #"{
""0"": {
""vk"": {
""cost"": 19,
""count"": 1903
},
""ok"": {
""cost"": 4,
""count"": 2863
},
""wa"": {
""cost"": 4,
""count"": 2210
}
},
""1"": {
""vk"": {
""cost"": 11,
""count"": 942
},
""ok"": {
""cost"": 68,
""count"": 1153
},
""wa"": {
""cost"": 14,
""count"": 7643
}
}
}";
This will spit out a response like this.
Id: 0
SubCode: vk
Cost: 19
Count: 1903
SubCode: ok
Cost: 4
Count: 2863
SubCode: wa
Cost: 4
Count: 2210
Id: 1
SubCode: vk
Cost: 11
Count: 942
SubCode: ok
Cost: 68
Count: 1153
SubCode: wa
Cost: 14
Count: 7643
There was the question about sorting, filtering and querying the list of different stores based on either counts or costs. Following is a list pattern that you can use and use LINQ to do the queries and filtering.
public class ItemInfo
{
[JsonProperty("cost")]
public int Cost { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
public class AllProdcuts
{
[JsonProperty("vk")]
public ItemInfo VK { get; set; }
[JsonProperty("ok")]
public ItemInfo OK { get; set; }
[JsonProperty("wa")]
public ItemInfo WA { get; set; }
}
public class Stores
{
[JsonProperty("ID")]
public string StoreID { get; set; }
[JsonProperty("store")]
public AllProdcuts Store { get; set; }
}
and this is how you would call it
string jsonDoc = #"{
""0"": {
""vk"": {
""cost"": 19,
""count"": 1903
},
""ok"": {
""cost"": 4,
""count"": 2863
},
""wa"": {
""cost"": 4,
""count"": 2210
}
},
""1"": {
""vk"": {
""cost"": 9,
""count"": 3
},
""ok"": {
""cost"": 4,
""count"": 63
},
""wa"": {
""cost"": 40,
""count"": 210
}
}
}";
var obj = JObject.Parse(jsonDoc);
List<Stores> allStores = new List<Stores>();
foreach (var property in obj.Properties())
{
string storeNumber = property.Name;
allStores.Add(new Stores() { StoreID = property.Name, Store = JsonConvert.DeserializeObject<AllProdcuts>(obj[property.Name].ToString()) });
}
// If you want to get list of <count, cost> for all stores
List<ItemInfo> totalItemInAllStores = allStores.Select(x => x.Store.OK).ToList();
int totalOKInAllStores = allStores.Sum(x => x.Store.OK.Count);
int totalWAInAllStores = allStores.Sum(x => x.Store.WA.Count);
int totalOkInXStore = allStores.FirstOrDefault(x => x.StoreID.Equals("0")).Store.OK.Count;
string storeWithHighestCountOfOK = allStores.OrderBy(x => x.Store.OK.Count).Last().StoreID;
You can create separate methods for different sorting/queries you want to perform on each of the items for ease of getting the numbers you want.

How to query into a JSON getting using LINQ?

JSON
{
"count": 3,
"value": [
{
"id": "AAAAAAAAAAAAA",
"description": "test1",
"name": "name1"
},
{
"id": "BBBBBBBBBB",
"description": "test2",
"name": "name2"
},
{
"id": "CCCCCCCCCCCC",
"description": "test3",
"name": "name3"
}
]
}
I have a code in my solution retrieving from a LIST api and giving the JSON above.
How can I use a LINQ to retrieve specific values? (e.g) I need to select name1 and I will get the id,description,name values.
I am using a dynamic variable in my code:
dynamic json = JObject.Parse(client.GetString().Result);
I'd been tinkering with other online guides the past few hours. However, can't get the result right.
Please help.
One solution would be to deserialize your JSON string into C# objects and then use Linq to get a specific object.
C# class definitions:
public class Content
{
[JsonProperty("count")]
public int Count { get; set; }
[JsonProperty("value")]
public List<Value> Values { get; set; }
public Content()
{
Values = new List<Value>();
}
}
public class Value
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
Deserializing and getting the object:
string json = #"{
""count"": 3,
""value"": [
{
""id"": ""AAAAAAAAAAAAA"",
""description"": ""test1"",
""name"": ""name1""
},
{
""id"": ""BBBBBBBBBB"",
""description"": ""test2"",
""name"": ""name2""
},
{
""id"": ""CCCCCCCCCCCC"",
""description"": ""test3"",
""name"": ""name3""
}
]
}";
Content content = JsonConvert.DeserializeObject<Content>(json);
Value value = content.Values.FirstOrDefault(x => x.Name.Equals("name1", StringComparison.InvariantCultureIgnoreCase));
First, you can create a class to represent a client:
public class Client
{
public string Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
}
With this class, you can use JObject.Parse (as you're already doing) to parse the JSON into something that can be queried, use SelectToken to pull out the value array and then use ToObject to convert that to a list of Clients. Here's what that looks like:
var jsonObject = JObject.Parse(json_source);
var jsonObjectValue = jsonObject.SelectToken("value");
var clients = jsonObjectValue.ToObject<List<Client>>();
Once you've got your clients variable, you can use a simple LINQ statement to find the one that is name1:
var clientWithName1 = clients.SingleOrDefault(x => x.Name == "name1");
In this case, clientWithName will be null if no such client was found.
Here's a dotnetfiddle that demonstrates a complete solution.
Create an object Client that has properties id, description and name. Deserialize the json into a list of these objects.
List<Client> clients = JsonConvert.Deserialize<List<Client>>(json_source);
string desc = clients[0].description;
Apparently with fiddling with my code I found an answer for myself.
Thanks for to the ones trying to help me for giving me ideas.
var requestWorkProcess = await client.GetStringAsync("my url");
var workProcessId = JObject.Parse(requestWorkProcess)["value"].Children<JObject>().FirstOrDefault(o => o["name"].ToString() == workProcess).GetValue("id");

JSON deserialization for polymorphic results

I have the following problem which is driving me crazy to find a proper solution.
I have to consume two RESTful APIs which return the same structure except for the items structure.
Let me give you two examples:
{
"hasmoredata":true,
"current_page": 1,
"page_size": 20,
"total_pages": 5,
"items": [
{
"user_id": "1",
"username": "carl",
"first_name": "carl",
}
]
}
{
"hasmoredata":true,
"current_page": 1,
"page_size": 10,
"total_pages": 2,
"items": [
{
"course_id": "10",
"course_name": "Math",
"duration": "3h",
}
]
}
I'd like to have two classes which extend an abstract one that collect the common properties. Something like this (in C#):
public abstract class CursorResult
{
[JsonProperty("current_page")]
public int CurrentPage { get; set; }
[JsonProperty("page_size")]
public int PageSize { get; set; }
[JsonProperty("total_pages")]
public int TotalPages { get; set; }
[JsonProperty("hasmoredata")]
public bool HasMoreData{ get; set; }
}
public class UsersList : CursorResult
{
[JsonProperty("items")]
List<User> Users { get; set; }
}
public class CoursesList : CursorResult
{
[JsonProperty("items")]
List<Courses> Courses { get; set; }
}
Now the problem is the fact that i have to write a function that collect the entire result (all the pages of items) and merge those results in one:
private CursorResult GetEntireResult(string apiURL)
{
Cursor c = new Cursor(1, pageSize);
CursorResult result = TryDeserializeCursorResult(CallRestFulAPI(apiURL + c.GetCursorParametersString, Method.GET));
c.Hashcode = result.CursorHashCode;
while (result.HasMoreData)
{
c.CurrentPage += 1;
result.AddItems(TryDeserializeCursorResult(CallRestFulAPI(apiURL + c.ParametersString, Method.GET)));
}
return result;
}
but i don't have any idea on how write the AddItem function in order to add Users or Courses depending on the API result.
Thanks in advance for any help!
Lapo
A couple of things:
With your current code, assuming you're using a default serializer, when you deserialize you're not going to capture 'items' because you're deserializing to CursorResult which doesn't have an 'items' property defined. The serializer doesn't automatically know about derived types. Also I would recommend renaming the UserList and CoursesList classes to UserCursorResult/CourseCursorResult. The classes aren't lists, they contain lists.
Here is code that will discriminate between the json serialized (sub)types :
string yourJsonString = #"{ 'hasmoredata':true,'current_page':
1,'page_size': 20,'total_pages': 5,'items': [{'user_id':
'1','username': 'carl','first_name': 'carl'}]}";
JSchemaGenerator generator = new JSchemaGenerator();
JSchema userSchema = generator.Generate(typeof(UsersList));
JSchema courseSchema = generator.Generate(typeof(CoursesList));
JToken jObject = JObject.Parse(yourJsonString);
if (jObject.IsValid(courseSchema))
{
CoursesList courseList = JsonConvert.DeserializeObject<CoursesList>(yourJsonString);
//do stuff with CourseList
}
else if (jObject.IsValid(userSchema))
{
UsersList userList = JsonConvert.DeserializeObject<UsersList>(yourJsonString);
//do stuff with UsersList
}

How to deserialize json string in windows phone?

I just got my json response as a string.My json is given below,
"code": 0,
"message": "success",
"students": {
"details":{
"hjeke": {
"id": "257633000000070001",
"name": "hjeke",
"percentage": 36,
"type": "Good",
},
"Second": {
"id": "257633000000073001",
"name": "Second",
"percentage": 4,
"type": "bad",
}
}
}
Like hjeke and Second there are many key value pairs,how can i deserialize my json using Newtonsoft.json
Try to understand my solution in your previous question
How to deserialize json data in windows phone?
Your first JSON in that question was good and simple to use.
JSON, where field names are unique not convinient to deserialize. So, you got problems such as public class Hjeke and public class Second for each instance, when you use code generator.
Use JSON-structure with list of students:
"code": 0,
"message": "success",
"students": [
{
"id": "257633000000070001",
"name": "hjeke",
"percentage": 36,
"type": "Good",
},
{
"id": "257633000000073001",
"name": "Second",
"percentage": 4,
"type": "bad",
}]
is good and flexible structure. Using this, you don't need to parse not obvious fields like
"details":{
"hjeke": {
and so on.
And work with them using classes, from my previous answer. The main idea - you need list of objects. public List<StudentDetails> students. Then, all students objects deserialized in List, which is easy to use.
As everybody mentioned your json seems to be very unflexible, huh.
You can extract the data you are interested in.
So this is your model:
public class StudentDetails
{
public string id { get; set; }
public string name { get; set; }
public int percentage { get; set; }
public string type { get; set; }
}
And this is how you can extract it:
var jsonObj = JObject.Parse(str);
// get JSON result objects into a list
var results = jsonObj["students"]["details"].Children().Values();
// serialize JSON results into .NET objects
var details = new List<StudentDetails>();
foreach (JToken result in results)
{
var st = result.ToString();
var searchResult = JsonConvert.DeserializeObject<StudentDetails>(st);
details.Add(searchResult);
}
I'm using a newtonsoft.json library here.
Your Response string has some mistakes man, its not a valid json
just small modification to be done as below:
{
"code": 0,
"message": "success",
"students": {
"details": {
"hjeke": {
"id": "257633000000070001",
"name": "hjeke",
"percentage": 36,
"type": "Good"
},
"Second": {
"id": "257633000000073001",
"name": "Second",
"percentage": 4,
"type": "bad"
}
}
}
}
you can make out the difference
Now Follow these steps:
1.Go to this link Json to C#
2.place your Json string there and generate C# class object
3.Now create this class in your solution
4.Now deserialize As below
var DeserialisedObject = JsonConvert.DeserializeObject<Your Class>(YourJsonString);
First, create the classes:
public class Hjeke
{
public string id { get; set; }
public string name { get; set; }
public int percentage { get; set; }
public string type { get; set; }
}
public class Second
{
public string id { get; set; }
public string name { get; set; }
public int percentage { get; set; }
public string type { get; set; }
}
public class Details
{
public List<Hjeke> hjeke { get; set; }
public List<Second> Second { get; set; }
}
public class Students
{
public List<Details> details { get; set; }
}
public class RootObject
{
public int code { get; set; }
public string message { get; set; }
public List<Students> students { get; set; }
}
After that, use JSON.NET to deserialize:
var deserialized = JsonConvert.DeserializeObject<Class1>(YourStringHere);
Do you have any influence over the json response? Details should probably be a JSONArray in this case, not an object with a varying amount of properties, since I assume that's what you mean is the issue here.

Categories