Deserialize jagged array with unknown dimensions - c#

I'm currently trying to parse a Json file in Unity using Json.NET and can parse 80% of the data at the moment. I've come across a section that is formatted like this.
{
"features": [
{
"coordinates": [
[
[ 1, 1 ],
[ 2, 2 ]
]
]
},
{
"coordinates": [
[ 1, 1 ],
[ 2, 2 ],
[ 3, 3 ]
]
},
{
"coordinates": [
[
[ 1, 2 ],
[ 1, 2 ]
]
]
},
{
"coordinates": [
[
[
[ 1, 2 ],
[ 1, 2 ]
]
]
]
}
]
}
The coordinates array may contain an unknown number of coordinates and it will be a jagged array with unknown dimensions as well. I'm unable to parse it as I'm not very well versed with Json deserialization.
Any help on how to approach this appreciated.

I'm not sure how much variety is in your data but here's a rough idea using recursion.
void ParseCoordinateJSON()
{
var coordinates = new List<int[]>();
var parsed = JObject.Parse(rawJson);
var featureArray = parsed.SelectToken("features");
var coordArray = featureArray.Children();
coordArray.ToList().ForEach(n => ExtractCoordinates(n.SelectToken("coordinates"), coordinates));
Debug.Log(string.Join("\n", coordinates.Select(c => $"({string.Join(", ", c)})")));
}
void ExtractCoordinates(JToken node, List<int[]> coordinates)
{
if (node == null)
{
return;
}
if (node.Children().Any(n => n.Type == JTokenType.Integer))
{
coordinates.Add(node.Children().Select(n => n.Value<int>()).ToArray());
return;
}
node.Children().Where(n => n.Type == JTokenType.Array).ToList().ForEach(n => ExtractCoordinates(n, coordinates));
}
Edit:
Here's it is without linq which might be easier to follow:
void ParseCoordinateJSONNoLinq()
{
var coordinates = new List<int[]>();
var parsed = JObject.Parse(rawJSON);
var featureArray = parsed.SelectToken("features");
// These will be the objects with a "coordinates" key and an array value.
var coordArray = featureArray.Children();
foreach(var node in coordArray)
{
ExtractCoordinatesNoLinq(node.SelectToken("coordinates"), coordinates);
}
Console.WriteLine(string.Join("\n", coordinates.Select(c => $"({string.Join(", ", c)})")));
}
void ExtractCoordinatesNoLinq(JToken node, List<int[]> coordinates)
{
var intValues = new List<int>();
// Step through each child of this node and do something based on its node type.
foreach(var child in node.Children())
{
// If the child is an array, call this method recursively.
if (child.Type == JTokenType.Array)
{
// Changes to the coordinates list in the recursive call will persist.
ExtractCoordinatesNoLinq(child, coordinates);
// The child type is an integer, add it to the int values.
} else if (child.Type == JTokenType.Integer)
{
intValues.Add(child.Value<int>());
}
}
// Since we found int values at this level, add them to the shared coordinates list.
if (intValues.Count > 0)
{
coordinates.Add(intValues.ToArray());
}
}
If the rest of your data is reliable, I would use typical data objects and de-serialize to them then add something like the above as a Custom JSON Converter for an object representing the jagged array.
public class MyDataObject {
public string SomeField {get; set;}
public Vector2 Position {get; set;}
[JsonProperty("features")]
public JaggedFeatures {get; set;}
}
public class JaggedFeatures {
public List<int[]> Coordinates {get; set;}
}
//...
JsonConvert.Deserialize<MyDataObject>(rawJSON, new JaggedFeaturesConverter())

Related

Deserializing JObject that has no property name

I want to be able to print the type, name and the sub entries, but I'm at a a loss as to how. Can I access specific positions of an array instead of naming the property I want?
The main property has entry properties, like mainProperty.entries, and I can pull the strings out using a foreach. But when the "entries" has properties that don't have a name, I have no idea how to access them.
I know that the "Entry text here" are JValues, and that the others are JObjects.
The entry without objects can be accessed through mainProperty.entries.
Since the other type doesn't have a name, I don't know how to access the type, name and "sub" entries. (mainProperty.JObject.type)
{
"mainProperty": [
{
"entries": [
"Entry text here",
{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
},
{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
}
]
}
]
}
{
"mainProperty": [
{
"entries": [
"Entry text here",
"Second line of text"
]
}
]
}
foreach (var entry in mainProperty.entries)
{
debugOutput(entry.GetType().ToString());
//debugOutput("\t" + (string)entry);
}
The output should be:
First example:
Entry text here
**Entry of entry** Entry text here
**Entry of entry** Entry text here
Second example:
Entry text here
Second line of text
In your JSON, entries is an array of mixed types, where the values can either be strings or objects. If they are objects, each child object has an entries property whose value is also an array. In your examples it seems that this inner array will always contain strings, but it looks like it could actually be a fully recursive structure, where sub-entries can contain sub-sub-entries and so on. If that is so, you would need a recursive method to dump out the entries. Something like this could work:
public static void DumpEntries(JObject obj, string indent = "")
{
JArray entries = (JArray)obj["entries"];
if (entries != null)
{
foreach (JToken entry in entries)
{
if (entry.Type == JTokenType.String)
{
debugOutput(indent + entry.ToString());
}
else if (entry.Type == JTokenType.Object && (string)entry["type"] == "entries")
{
debugOutput(indent + "**" + (string)entry["name"] + "**");
DumpEntries((JObject)entry, indent + " ");
}
}
}
}
Then use it with your JSON like this:
var root = JObject.Parse(json);
foreach (JObject obj in (JArray)root["mainProperty"])
{
DumpEntries(obj);
}
Fiddle: https://dotnetfiddle.net/MjRNGn
In answer to your original question,
Can I access specific positions of an array instead of naming the property I want?
Yes, you absolutely can do this.
For example, if you wanted to get the first sub-entry of the third entry, you could do this:
var root = JObject.Parse(json);
var sub = root["mainProperty"][0]["entries"][2]["entries"][0];
debugOutput((string)sub);
Fiddle: https://dotnetfiddle.net/sMyMxH
If you modify your JSON data like this:
var response = {
"mainProperty": [{
"entries": [{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
},
{
"type": "entries",
"name": "Entry of entry",
"entries": [
"Entry text here"
]
}
]
}]
}
You must create Response object but first, you need add NewtonSoft.Json to your project from NuGet. After add this library to your project you need to create Response object like this:
public class Entry
{
public string type { get; set; }
public string name { get; set; }
public List<string> entries { get; set; }
}
public class MainProperty
{
public List<Entry> entries { get; set; }
}
public class RootObject
{
public List<MainProperty> mainProperty { get; set; }
}
Than you can access your data:
var _res = JsonConvert.DeserializeObject<RootObject>(response.ToString());
foreach(var item in _res.mainProperty.entries.entries)
{
debugOutput(item);
}

Json combine two files for a large value

Here are two json samples.
I want to combine this json into a file.
If a key exists in a value that is combined without thinking, it is difficult to replace only the value that is high in value.
First Sample.
{
"employees": [
{
"firstName": "Tom",
"HighValue": "3"
},
{
"firstName": "Maria",
"HighValue": "4"
},
{
"firstName": "Robert",
"HighValue": "45"
}
]
}
Second Sample.
{
"employees": [
{
"firstName": "Tom",
"HighValue": "6"
},
{
"firstName": "Maria",
"HighValue": "4"
},
{
"firstName": "Robert",
"HighValue": "45"
},
{
"firstName": "John",
"HighValue": "1"
}
]
}
I want Result:
{
"employees": [
{
"firstName": "Tom",
"HighValue": "6"
},
{
"firstName": "Maria",
"HighValue": "4"
},
{
"firstName": "Robert",
"HighValue": "45"
},
{
"firstName": "John",
"HighValue": "1"
}
]
}
The goal is to combine two samples, Json, into one result json. What's the best way?
One simple approach would be by making use of a json framework like Json.NET which handles serialization/deserialization for you.
First create a data model into which your json data can be deserialized to. I used the online tool json2csharp for this. This will give you the following model:
public class Employee
{
public string firstName { get; set; }
public int HighValue { get; set; }
}
public class RootObject
{
public List<Employee> employees { get; set; }
}
Now you can simply deserialize your json strings into objects like this:
string json1 = "";
string json2 = "";
var obj1 = JsonConvert.DeserializeObject<RootObject>(json1);
var obj2 = JsonConvert.DeserializeObject<RootObject>(json2);
After this step you just have to iterate over your employees, check if they exist in both lists and add/update them accordingly:
foreach(var emp in obj2.employees)
{
Employee existing = null;
try
{
existing = obj1.employees.SingleOrDefault(e => e.firstName == emp.firstName);
}
catch(Exception ex)
{
// The same employee exists multiple times in the first list
}
if(existing != null)
{
// obj1 already contains an employee with the given name, check which value is bigger
if(existing.HighValue < emp.HighValue)
{
// The value of the existing employee is smaller
// -> Update the value with the value from the second object
existing.HighValue = emp.HighValue;
}
}
else
{
// obj1 does not already contain an employee with the given name, add the whole employee
obj1.employees.Add(emp);
}
}
Now obj1 contains the combined list of employees.
To serialize the combined list back to json do the following:
var json = JsonConvert.SerializeObject(obj1);
(For the record JSON isn't one of my strongest points but I thought answering this for you would be a good challenge)
The following code takes both JSON samples as JObjects using Newtonsoft.Json, then merges the two samples together. I then use LINQ to group and select the people with the highest values and output a new array.
The advantage to this solution is if you only want to use C# to merge, not use it for any other logic, you don't need to deserialize to an object.
Give it a try:
var firstSample = JObject.Parse("Your first sample goes here");
var secondSample = JObject.Parse("Your second sample goes here");
var newJsonArray = new JArray(); //Makes new array for output
firstSample.Merge(secondSample); //Merges the firstSample JObject with secondSample JObject
var filteredJson = firstSample["employees"] //Makes new JObject with filtered values
.GroupBy(x => x["firstName"]) // Groups by the first name
.Select(x => x.Where(y => y["HighValue"] == x.Max(z => z["HighValue"]))) // Select the employee in each group with the highest value
.ToList(); // Converts to a list out
foreach (var token in filteredJson)
{
newJsonArray.Add(token); // For each selected employee, add it to the new array
}
var outputJson = new JObject(new JProperty("employees", newJsonArray)); // Composes the new JSon string

There is no argument that corresponds to the required formal parameter

This is probably not the best way to be doing this but it's the best I know how to do with c#. I'm trying to create a dictionary and then convert it to json later. Right now I"m just trying to get the dictionary to match what I want in the json format later. Here is what I have so far:
`Dictionary<Dictionary<string, string>, Dictionary<string, List<List<Decimal>>>> testDict = new Dictionary<Dictionary<string, string>, Dictionary<string, List<List<Decimal>>>>() {
new Dictionary<string, string>() {
{ "test", "test" }
}
};`
This is giving me the following error:
There is no argument that corresponds to the required formal parameter
I don't know what could be causing this and any help would be great, thanks!
Here is the json structure I'm trying to replicate:
[
{
"target": "1",
"datapoints": [
[
67.0,
1491609600.0
]
]
},
{
"target": "2",
"datapoints": [
[
54.0,
1491091200.0
],
[
65.0,
1491177600.0
],
[
69.0,
1491609600.0
],
[
65.0,
1491696000.0
],
[
54.0,
1491868800.0
],
[
63.0,
1491955200.0
],
[
64.0,
1492214400.0
],
[
57.0,
1492732800.0
],
[
72.0,
1492819200.0
],
[
50.0,
1493337600.0
],
[
63.0,
1493424000.0
]
]
},
]
Ok, not the answer to your question, but it will help, is the correct structure for your JSON:
public class TargetClass
{
public string target{ get; set; }
public List<double[]> datapoints{ get; set; }
}
That's the base class. If you want to deserialize what you have in JSON you will do something like this (assuming you are using Newtonsoft Json, else change to the library you use):
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<TargetClass[]>(theString);
And to serialize you would create something like this:
var items = new List<TargetClass>();
var target = new TargetClass{ target = "1", datapoints = new List<double[]>{ new double[]{ 67.0, 1491609600.0 } };
items.Add(target);
var ser = Newtonsoft.Json.JsonConvert.SerializeObject(items);
Using anonymous types you can create complex object hierarchies almost as easy as writing plain JSON:
var obj = new[] {
new {
target = "1",
datapoints = new [] {
new [] {
67.0,
1491609600.0
}
}
},
new {
target = "2",
datapoints = new [] {
new [] {
54.0,
1491091200.0
},
new [] {
65.0,
1491177600.0
},
}
}
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Demo: https://dotnetfiddle.net/jk8gho
If subcollections in this object are a subject to expansion, these collections should better be explicitly defined as List<dynamic>. Strictly speaking this dynamic is not necessary and explicit types may be used instead, but using dynamic simplifies the definition.
var obj = new List<dynamic> {
new {
target = "1",
datapoints = new List<dynamic> {
new [] {
67.0,
1491609600.0
}
}
},
new {
target = "2",
datapoints = new List<dynamic> {
new [] {
54.0,
1491091200.0
},
new [] {
65.0,
1491177600.0
},
}
}
};
var target2 = obj.Where(t => t.target == "2").Single();
target2.datapoints.Add(new [] {
64.0,
1492214400.0
});
target2.datapoints.Add(new[] {
57.0,
1492732800.0
});
var target3 = new {
target = "3",
datapoints = new List<dynamic> { }
};
target3.datapoints.Add(new[] {
72.0,
1492819200.0
});
obj.Add(target3);
var json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Demo: https://dotnetfiddle.net/d4ZUH8

How to Serialize Tree Structure Data From Database to JSON

I have data in a data store which represents a tree structure. Following code represents how it can be described in a simple DataTable in C#.
using System.Data;
namespace DemoConsoleApp
{
class Program
{
static void Main(string[] args)
{
DataTable treeData = new DataTable();
treeData.Clear();
treeData.Columns.Add("NodeId", typeof(int));
treeData.Columns.Add("ParentNodeId", typeof(int));
treeData.Columns.Add("Data", typeof(string));
treeData.Columns.Add("AbsolutePath", typeof(string));
DataRow record1 = treeData.NewRow();
record1["NodeId"] = 1;
record1["ParentNodeId"] = 0;
record1["Data"] = "Dairy";
record1["AbsolutePath"] = "/Dairy/";
treeData.Rows.Add(record1);
DataRow record2 = treeData.NewRow();
record2["NodeId"] = 2;
record2["ParentNodeId"] = 1;
record2["Data"] = "Yoghurt";
record2["AbsolutePath"] = "/Dairy/Yoghurt";
treeData.Rows.Add(record2);
DataRow record3 = treeData.NewRow();
record3["NodeId"] = 3;
record3["ParentNodeId"] = 1;
record3["Data"] = "Cheese";
record3["AbsolutePath"] = "/Dairy/Cheese";
treeData.Rows.Add(record3);
DataRow record4 = treeData.NewRow();
record4["NodeId"] = 4;
record4["ParentNodeId"] = 2;
record4["Data"] = "Flavored";
record4["AbsolutePath"] = "/Dairy/Yoghurt/Flavored";
treeData.Rows.Add(record4);
}
}
}
I need to serialize the data into the following structure.
{
"TreeData": {
"NodeId" : "1",
"Data" : "Dairy",
"Children": [
{
"NodeId": "2",
"Data": "Yohurt",
"Children": [
{
"NodeId": "4",
"Data": "Flavored",
"Children": []
}
]
},
{
"NodeId": "3",
"Data": "Cheese",
"Children": []
}
]
}
}
How do i go about achieving this?
Create a new class:
class Node
{
public int NodeId;
public int ParentNodeId;
public string Data;
public string AbsolutePath;
public List<Node> Children = new List<Node>();
}
Then iterate over rows of the DataTable, create an appropriate tree structure and simply call JsonConvert.SerializeObject()
var nodes = new Dictionary<int, Node>();
foreach(DataRow record in treeData.Rows)
{
var node = new Node { NodeId = (int)record["NodeId"], ParentNodeId = (int)record["ParentNodeId"], Data = (string)record["Data"], AbsolutePath = (string)record["AbsolutePath"] };
nodes.Add(node.NodeId, node);
}
var rootNodeId = 1;
var rootNode = nodes[rootNodeId];
foreach(var keyValuePair in nodes)
{
var node = keyValuePair.Value;
if(node.NodeId != rootNodeId)
{
nodes[node.ParentNodeId].Children.Add(node);
}
}
string json = JsonConvert.SerializeObject(rootNode, Formatting.Indented);
Debug.WriteLine(json);
Output:
{
"NodeId": 1,
"ParentNodeId": 1,
"Data": "Dairy",
"AbsolutePath": "/Dairy/",
"Children": [
{
"NodeId": 2,
"ParentNodeId": 1,
"Data": "Yoghurt",
"AbsolutePath": "/Dairy/Yoghurt",
"Children": [
{
"NodeId": 4,
"ParentNodeId": 1,
"Data": "Flavored",
"AbsolutePath": "/Dairy/Flavored",
"Children": []
}
]
},
{
"NodeId": 3,
"ParentNodeId": 1,
"Data": "Cheese",
"AbsolutePath": "/Dairy/Cheese",
"Children": []
}
]
}
you need to make custom recursive function with Serialization Class
which will append Children till you get leaf node.
Below is example for that. please use your logic to append JsonString
var appendJsonString;
Private string PassParentId(int Id)
{
var childnodecount=0;
childnodecount=getchildnode(Id);
while(childnodecount>0)
{
getchilddata=getchildnode(Id);
childnodecount=getchilddata.count();
appendJsonString = getchilddata();
}
return appendJsonString;
}
My aproach would be to create an object (TreeDataNode, for example) and giving this object the following variables
public class TreeDataNode{
int nodeId;
string data;
List<TreeDataNode> children;
public void AddChildren(string relativePath, TreeDataNode node)
{
string[] path = relativePath.Split('/');
if(path.Count<2)
{
children.Add(node);
}else
{
string newPath = String.Join(#"/", relativePath.Split('\\').Skip(1));
children.First(n=> n.data==path[0]).AddChildren(newPath,node);
}
}
}
Once you populate this object, having a parent TreeDataNode and the subsequent children, you just have to serialize it with any standard JSON serializer, and you would have a structure just like the one you are trying to achieve.
Now, you just need to be sure to add your objects in order, never a parent after his children, and it will be alright. Create your parent TreeDataNode with the root object, and then call AddChildren for every object on this parent.

c# NewtonJson Jarray check null/empty error

How we can check a json array is null or emty?
Json:
{
"productList": [
{
"id": 2440,
"serviceStatus": 1,
"listOfBillProductsExtras": [
{
"id": 2441,
"amount": 1,
"balance": 2,
}
],
"deskName": "Desk 1",
"onlyTime": "15:25"
},
{
"id": 2441,
"serviceStatus": 1,
"listOfBillProductsExtras": [ ],
"deskName": "Desk2",
"onlyTime": "15:27"
}
]
}
I try
JArray productList = JArray.Parse(content["productList"].ToString());
but it didn't work. (There was exp. Null Referance ) So, I want to check listOfBillProductsExtras array is null or empty. If not empty I will get the id, amount, balance.
Parse the Json object to jArray:
public ActionResult Method(object[] data)
{
var productList = Json.ParseJsonObjectToJArray(data, "productList");
if(jArray.Count > 0)
{
}
}
public class Json
{
public static JArray ParseJsonObjectToJArray(object[] data, string objectName)
{
dynamic jObject = JObject.Parse(data[0].ToString());
var info = jObject[objectName];
return info;
}
}
This should work
var found = JObject.Parse(json).SelectToken("productList[0].listOfBillProductsExtras[0].id");
where json is your input string.
found variable can be checked for null value.

Categories