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);
}
Related
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())
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
I see many questions and answers about what I'm trying to do but after reading the answers I'm not able to get the key and value out of this json.
Here is the json being returned:
{
"#odata.context": "https://con813-crm612cf723bbf35af6devaos.cloudax.dynamics.com/data/$metadata#Customers(CustomerAccount,Name)",
"value": [
{
"#odata.etag": "W/\"JzAsMjI1NjU0MjE1NTg7MCwwOzAsNTYzNzE0NTMyODswLDU2MzcxNDQ1NzY7MCwyMjU2NTQyNTY5MzswLDIyNTY1NDI3MjM2OzAsMDswLDIyNTY1NDI3MjM2OzAsMjI1NjU0MjcyMzY7MCwwJw==\"",
"CustomerAccount": "DE-001",
"Name": "Contoso Europe"
},
{
"#odata.etag": "W/\"JzAsMjI1NjU0MjE1NTk7MCwwOzAsMzU2MzcxNDkxMTI7MCw1NjM3MTQ0NTc3OzAsMjI1NjU0MjU2OTQ7MCwyMjU2NTQyNzIzODswLDA7MCwyMjU2NTQyNzIzODswLDIyNTY1NDI3MjM4OzAsMCc=\"",
"CustomerAccount": "US-001",
"Name": "Contoso Retail San Diego"
},
{
"#odata.etag": "W/\"JzAsMjI1NjU0MjE1NjA7MCwwOzAsMzU2MzcxNDkxMTM7MCw1NjM3MTQ0NTc4OzAsMjI1NjU0MjU2OTU7MCwyMjU2NTQyNzI0MDswLDA7MCwyMjU2NTQyNzI0MDswLDIyNTY1NDI3MjQwOzAsMCc=\"",
"CustomerAccount": "US-002",
"Name": "Contoso Retail Los Angeles"
}
]
}
I need to get the names of the keys, which is "CustomerAccount" and "Name" in this example, and then their values. I can't figure out to just return those values.
JObject parsedJson = JObject.Parse(_json);
StringBuilder builder = new StringBuilder();
foreach (JProperty property in parsedJson.Properties())
{
builder.Append((string.Format("Name: [{0}], Value: [{1}].", property.Name, property.Value)));
}
Hoping to add more clarity; In this example I would like to write out the key/values after "#odata.etag" which the keys are "CustomerAccount" and "Name" and their values are after the colon. The keys/values are dynamic so I need to loop through writing out whatever the key names and values are after each "#odata.etag" value.
Use JsonConvert.DeserializeObject and pass the type of object you want to parse. there was a missing closing curly brace at the end. I hope I understood what you want to do correctly, if not plz leave a comment
class Value
{
public string CustomerAccount { get; set; }
public string Name { get; set; }
}
class Customer
{
public List<Value> Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
var obj = JsonConvert.DeserializeObject<Customer>(#" {
'#odata.context': 'https://con813-crm612cf723bbf35af6devaos.cloudax.dynamics.com/data/$metadata#Customers(CustomerAccount,Name)',
'value': [
{
'#odata.etag': 'W/\'JzAsMjI1NjU0MjE1NTg7MCwwOzAsNTYzNzE0NTMyODswLDU2MzcxNDQ1NzY7MCwyMjU2NTQyNTY5MzswLDIyNTY1NDI3MjM2OzAsMDswLDIyNTY1NDI3MjM2OzAsMjI1NjU0MjcyMzY7MCwwJw==\'',
'CustomerAccount': 'DE-001',
'Name': 'Contoso Europe'
},
{
'#odata.etag': 'W/\'JzAsMjI1NjU0MjE1NTk7MCwwOzAsMzU2MzcxNDkxMTI7MCw1NjM3MTQ0NTc3OzAsMjI1NjU0MjU2OTQ7MCwyMjU2NTQyNzIzODswLDA7MCwyMjU2NTQyNzIzODswLDIyNTY1NDI3MjM4OzAsMCc=\'',
'CustomerAccount': 'US-001',
'Name': 'Contoso Retail San Diego'
},
{
'#odata.etag': 'W/\'JzAsMjI1NjU0MjE1NjA7MCwwOzAsMzU2MzcxNDkxMTM7MCw1NjM3MTQ0NTc4OzAsMjI1NjU0MjU2OTU7MCwyMjU2NTQyNzI0MDswLDA7MCwyMjU2NTQyNzI0MDswLDIyNTY1NDI3MjQwOzAsMCc=\'',
'CustomerAccount': 'US-002',
'Name': 'Contoso Retail Los Angeles'
}
]
}");
foreach (var value in obj.Value)
{
Console.WriteLine($"Name: 'Name' Value: {value.Name}");
Console.WriteLine($"Name: 'CustomerAccount' Value: {value.CustomerAccount}");
}
}
}
If you don't know the keys (properties are dynamic) of the object, you could use the following code snippet but it needs a change in the class declaration.
class Customer
{
//this is list of value objects (value is a dictionary)
public List<Dictionary<String,String>> Value { get; set; }
}
And here's how to deserialize and loop through the array of values
var obj = JsonConvert.DeserializeObject<Customer>(myString);
foreach (var value in obj.Value)
{
foreach (var key in value)
{
if (key.Key == "#odata.etag")
continue;
Console.WriteLine("Name: [{0}], Value: [{1}]",key.Key, key.Value);
}
}
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.
I use the DataContractJsonSerializer to parse a json string into a object hierarchie.
The json string looks like this:
{
"groups": [
{
"attributes": [
{
"sortOrder": "1",
"value": "A"
},
{
"sortOrder": "2",
"value": "B"
}
]
},
{
"attributes": {
"sortOrder": "1",
"value": "C"
}
}
]
}
As you can see the sub value of "attributes" can be an array or a single item.
I found the code part where the problem occures:
[DataContract]
public class ItemGroup
{
[DataMember(Name="attributes")]
public List<DetailItem> Items { get; set; }
}
This works for the first one but fails on the second one.
Has anyone an answer for this?
Thx
If you control how the JSON is created then make sure that attributes is an array even if it only contains one element. Then the second element will look like this and parse fine.
{
"attributes": [{
"sortOrder": "1",
"value": "C"
}]
}
As Daniel said, if you can control the creation of Json, it is better to continue that way.
But if you can't, then you can use Json.Net library & the JsonObject class from
this link to write some code like:
JObject o = (JObject)JsonConvert.DeserializeObject(input);
dynamic json = new JsonObject(o);
foreach (var x in json.groups)
{
var attrs = x.attributes;
if (attrs is JArray)
{
foreach (var y in attrs)
{
Console.WriteLine(y.value);
}
}
else
{
Console.WriteLine(attrs.value);
}
}
I tried to get this working with DataContractJsonSerializer, JavaScriptSerializer, and JSON.Net and none would deserialize directly to an object successfully in all cases. I used a similar approach as L.B, using JSON.Net, although without the use of dynamics and the extra JsonObject class. Adapting my code to your scenario would look something like the following:
private List<ItemGroup> ParseItemGroupList(string input)
{
JObject json = JObject.Parse(input);
List<ItemGroup> groups = new List<ItemGroup>();
JArray gArray = json["groups"] as JArray;
foreach (var gToken in gArray)
{
ItemGroup newGroup = new ItemGroup();
JToken attrToken = gToken["attributes"] as JToken;
if (attrToken is JArray)
{
newGroup.Items = attrToken.Children().Select(MapDetailItem()).ToList();
}
else
{
newGroup.Items = new List<DetailItem>() { MapDetailItem().Invoke(attrToken) };
}
groups.Add(newGroup);
}
return groups;
}
private static Func<JToken, DetailItem> MapDetailItem()
{
return json => new DetailItem
{
SortOrder = (string)json["sortOrder"],
Value = (string)json["value"]
};
}
Hopefully, someone will add a setting for JSON.Net to allow it to force deserialization to a collection with a single item rather than throwing an exception. It's a shame that you have to do all of the parsing manually when there is only one small portion of the JSON that doesn't parse correctly automatically.