I'm creating software where the users can create and load profiles to fill textboxes. The names and other information contained in the profile are stored in a JSON file. A profile name can contain any text that is entered by the user.
So for this, I'm trying to get each objects names of the JSON file (= each profile name) to display them in a treeview, but all I get is their contents.
I have a JSON file containing two objects:
[
{
"profile1": {
//Some informations 1
},
"profile2": {
//Some informations 2
}
}
]
For now, I have code that allows me to get the value of a given tag, but I can't find a way to get the name of each objects:
using (StreamReader r = File.OpenText(path))
{
string json = r.ReadToEnd();
dynamic array = JsonConvert.DeserializeObject(json);
foreach (var item in array)
{
debug_tb.Text += item.profile1; //Gives me each values of the "profile1 object"
}
}
So what I'm trying to get is to display "profile1" and "profile2" and "profile3" if it exists.
Your problem is that your JSON is an array with one object. So you can simplefy the JSON first:
{
"profile1": {
//Some informations 1
},
"profile2": {
//Some informations 2
}
}
Then you can easyly iterate over every item in the JSON and get the Name of it
dynamic array = JsonConvert.DeserializeObject("{ \"profile1\": { }, \"profile2\": { } }");
foreach (var item in array)
{
debug_tb.Text += item.Name; //Gives the name of the object
}
Console.WriteLine(text);
Console.ReadLine();
Related
How do I retrieve all of the [names] that are part of the object called [items] in this JSON scenario below in C# ?
I am using a shopping cart api and want to show the customer all of the items listed off that were part of the order once they get to the thank you page. The parent object I am looking at in the api is called items and it has child object data such as price of each item, name of each item and product image of each item etc.....
In my controller I was doing this below when I only needed to retrieve simple info like the order only has 1 invoiceNumber, 1 payment method, 1 shipping method, 1 promo code etc.. This code below works and gets me that data in that simple scenario below.
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
var obj = JObject.Parse(result);
//Order Info
string invoiceNumber = (string)obj["invoiceNumber"];
string email = (string)obj["email"];
string paymentMethod = (string)obj["paymentMethod"];
string shippingMethod = (string)obj["shippingMethod"];
//Discount info
string amountSaved = (string)obj["discounts"][0]["amountSaved"];
string promoCode = (string)obj["discounts"][0]["code"];
But now I need to tap into an object called [items] (the data for the items that were part of the order) that is going to have many names of items depending on the number of individual items that were part of the customer order. This is what I attempted but then realized that it would only return one record based on what number I used inside the brackets.
string items = (string)obj["items"][0]["name"]; //This returned the product at index 0
string items = (string)obj["items"][1]["name"]; //This returned the product at index 1
string items = (string)obj["items"][2]["name"]; //This returned the product at index 2
How do I write the syntax so that I can grab ALL of the name objects that currently reside in the item object for exaple? I know it has something to do with deserialize objects and list but I do not know the syntax and how to approach.
Any suggestions on how I can get this done?
the simpliest way
List<string> names = obj["items"].Select(x => (string) x["name"] ).ToList();
test
Console.WriteLine( string.Join(",",names)); // prod1,prod2
more complicated way is to create a c# class and deserialize data. But in this case you will be able to use Linq for queries and search
List<Item> items = obj["items"].Select(x => x.ToObject<Item>()).ToList()
public class Item
{
public string name { get; set; }
public double price { get; set; }
public int quantity { get; set; }
}
result (in json format)
[
{
"name": "prod1",
"price": 12.99,
"quantity": 4
},
{
"name": "prod2",
"price": 17.65,
"quantity": 1
}
]
You can use Newtonsoft json package to convert an object into JSON array.
Refer below link
http://www.newtonsoft.com/json/help/html/SerializingJSON.htm
I'm working on a Xamarin.Forms project with C# to connect to an OPC server and read values. I'm able to read the values, but I'm having trouble collating them into a list or array. After I do, I'd like to convert the values to ASCII.
Below is the code that is passed;
var readRequest = new ReadRequest
{
// set the NodesToRead to an array of ReadValueIds.
NodesToRead = new[] {
// construct a ReadValueId from a NodeId and AttributeId.
new ReadValueId {
// you can parse the nodeId from a string.
// e.g. NodeId.Parse("ns=2;s=Demo.Static.Scalar.Double")
NodeId = NodeId.Parse("ns=2;s=Link_CatConHybrid.2D.InStr1"),
//NodeId.Parse(VariableIds.Server_ServerStatus),
// variable class nodes have a Value attribute.
AttributeId = AttributeIds.Value
},
new ReadValueId
{
NodeId = NodeId.Parse("ns=2;s=Link_CatConHybrid.2D.InStr2"),
AttributeId = AttributeIds.Value
}
}
};
// send the ReadRequest to the server.
var readResult = await channel.ReadAsync(readRequest);
// DataValue is a class containing value, timestamps and status code.
// the 'Results' array returns DataValues, one for every ReadValueId.
DataValue dvr = readResult.Results[0];
DataValue dvr2 = readResult.Results[1];
Console.WriteLine("The value of Instr1 is {0}, InStr2 is {1}", dvr.Variant.Value, dvr2.Variant.Value);
What am I doing wrong or overlooking?
Edit: How would I combine all of the readResults into one ?
Just create a DataValue list and store them. Try like:
List<DataValue> endResult = new List<DataValue>();
foreach (DataValue value in readResult.Results)
{
endResult.Add(value);
}
Since Results is alerady collection of DataValue, you can just say
var dataValueCollection = readResult.Results; // if you want return collection you can just say return readResult.Results
If you are trying to write the values to console, then you can have loop directly on readResult.Results as below:
foreach(var dv in readResult.Results)
{
Console.WriteLine("The Value of InStr = {0}", dv.Variant.Value);
Console.WriteLine("The Value of InStr = {0}", dv.Variant.ReadValueId); // This line shows how to access ReadValueId.
// You can access other properties same as above
}
Working on writing a C# app to talk to our ServiceNow API. For some reason I can successfully ignore the root token of the JSON file it returns for the user, but not for the Incident. JSON returns seem to be almost identical save for the fact that one is from the incident table and the other is from the sys_user table.
Tried a couple of different ways of using Newtonsoft in the process of getting to the ignoring of the root token. Which has led me to where this code is at now (albeit unrefined)
void incidentCreator(string incID)
{
var restRequest = new RestRequest(_incTableLink + "/" + incID);
restRequest.AddParameter("sysparm_display_value", "true");
restRequest.AddParameter("sysparm_fields", "assigned_to,number,sys_updated_on,sys_id,comments_and_work_notes");
string incidentJsonString = _restInteractions.querySnow(restRequest);
Incident incident = null;
try
{
incident = JObject.Parse(incidentJsonString).SelectToken("result[0]").ToObject<Incident>();
}
catch (Exception e)
{
Console.WriteLine(e);
}
_incidentList.Add(incident);
}
void userGetter(string userID)
{
var restRequest = new RestRequest(_userTableLink);
restRequest.AddParameter("sysparm_query", "sys_id=" + userID);
string userJsonString = _restInteractions.querySnow(restRequest);
User user = null;
try
{
user = JObject.Parse(userJsonString).SelectToken("result[0]").ToObject<User>();
}
catch (Exception e)
{
Console.WriteLine(e);
}
//User user = JsonConvert.DeserializeObject<User>(userJsonString);
_userList.Add(user);
}
JSON string for incident that doesn't work
"{
\"result\": {
\"number\": \"INC1215653\",
\"sys_id\": \"52764be80007fb00903d51fea4ade50f\",
\"comments_and_work_notes\": \"2019-08-01 10: 46: 19 - PERSON (Additional comments)\\nEmailed PERSON
admin of the site to check permissions\\n",
\"sys_updated_on\": \"2019-08-01 10: 46: 17\",
\"assigned_to\": \"Employee\"
}
}"
JSON string for user which does work
"{
\"result\": [
{
\"calendar_integration\": \"1\",
\"last_position_update\": \"\",
\"u_lmc_account_indicator\": \"\",
\"u_supply_chain\": \"false\",
}
]
}"
No matter what I do with the incidentCreator() JSON parsing, using jsonconvert or the implementation present in current shared code. I either get "Object reference not set to an instance of an object." or just an object with all it's variables being null.
Any ideas? I'm sorry for the stripped back user json, it is a huge string and i couldn't be bothered cleaning it of PII before posting.
The JPath expression in your incidentCreator is incorrect.
It should be:-
try { incident = JObject.Parse(incidentJsonString).SelectToken("result").ToObject<Incident>(); }
Also, It's possibly just a result of pasting the JSON into the question, but the "comments_and_work_notes" field of the incident JSON example isn't escaped properly; just check you're receiving valid JSON using a linter to be sure.
The second JSON contents an array. You can see the différences [ (array) and { (object) after the word result
To parse for the case user, try it
JArray a = JArray.Parse(json);
var listUser = new List<User>();
foreach (JObject o in a.Children<JObject>())
{
foreach (JProperty p in o.Properties())
{
string name = p.Name;
string value = (string)p.Value;
var user = new User();
user.Name = name;
......
}
}
The difference is that your JSON string for incident has the field result which stores an object and the JSON string for user has the field result which contains an array of one object
incident
{
result :
{
...
}
}
user
{
result :
[
{
...
}
]
}
so the query SelectToken("result[0]") will work for the user since the result field contains an array with an element at index 0, but it wont work for the incident since there the result field does not conatin an array.
But SelectToken("result").ToObject<Incident>() should give you your Incident object.
I have this JSON:
{"id":1,"name":"Alabama"}
{"id":2,"name":"Alaska"}
{"id":3,"name":"Arizona"}
{"id":4,"name":"Arkansas"}
and this code:
string[] testDataFiles = new string[] { StatesOneFileName, StatesTwoFileName, ContinentsFileName };
foreach (var testFile in testDataFiles)
{
using (StreamReader r = new StreamReader(testFile))
{
string json = r.ReadToEnd();
dynamic jsonObjectArray = JsonConvert.DeserializeObject(json);
foreach (var item in jsonObjectArray)
{
expectedPartitions.Add(item.name);
}
}
}
When I run this, however, I get this error:
Additional text encountered after finished reading JSON content: {. Path '', line 2, position 0.
I've read a few StackOverflow suggestions to encase the test data in [], but unfortunately I cannot edit the json file itself. Is there a way to wrap something in my code such that it reads the correct way? Thanks.
Assuming a file that contains a json object per line, as follows:
{"id":1,"name":"Alabama"}
{"id":2,"name":"Alaska"}
{"id":3,"name":"Arizona"}
{"id":4,"name":"Arkansas"}
You can create the following model for your data:
public class State
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name{ get; set; }
}
And then create a simple method that reads the file line by line, as follows:
public static IEnumerable<State> GetStates(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new ArgumentException("Path to json file cannot be null or whitespace.", nameof(path));
}
if (!File.Exists(path))
{
throw new FileNotFoundException("Could not find json file to parse!", path);
}
foreach (string line in File.ReadLines(path).Where(x => !string.IsNullOrWhiteSpace(x)))
{
State state = JsonConvert.DeserializeObject<State>(line);
yield return state;
}
}
Using the code:
string path = ".....";
// get the list of State objects...
List<State> states = GetStates(path).ToList();
// or just the list of state names
List<string> stateNames = GetStates(path).Select(x => x.Name).ToList();
If this is your json file, it's not correctly formatted... You have multiple json objects in it, but it's neither enclosed in [] to show it's an array of json objects, nor are they delimited by comma.
The best thing you can do here, is to read line at a time, and convert it to json object, since it seems that each line represents one json object.
string json = "start";
using (StreamReader r = new StreamReader(testFile)) {
while (!String.IsNullOrWhiteSpace(json)){
json = r.ReadLine();
dynamic jsonObject = JsonConvert.DeserializeObject(json);
//DO YOUR MAGIC HERE
}
}
Here's one way you could easily fix the syntax errors in the json from that file, without modifying your existing code too much:
foreach (var testFile in testDataFiles)
{
// read the contents of the file with invalid json format, and build a new string named cleanJson with the fixed file contents
string[] fileLines = File.ReadAllLines(testFile);
string cleanJson = "["; // add an opening array bracket to make it valid json syntax
for (int i = 0; i < fileLines.Length; i++)
{
cleanJson += fileLines[i];
if (i < fileLines.Length - 1)
cleanJson += ","; // add a comma to the end of each line (except the last) to make it valid json syntax
}
cleanJson += "]"; // add a closing array bracket to make it valid json syntax
dynamic jsonObjectArray = JsonConvert.DeserializeObject(cleanJson);
foreach (var item in jsonObjectArray)
{
expectedPartitions.Add(item.name);
}
}
Here is the what I am trying to achieve here:
Get a list of file names and IDs from the database
Search for those files on a network path
Store any IDs of the files not found
Search for that ID in a second database
Search for those files on a network path
List all ID's where files are not found on either location.
The issue I an encountering is trying to use the file names from results I have collected.
When running the code, the raw JSON data collected from the database gets displayed, however when trying to list just the file names I get nothing (not even an error)
Any ideas on how to fix this and list the file names in a way that would also let me search for them later? Or is there a better way to do this?
Also note: due to the version of SQL server being used i have to use FOR XML as FOR JSON is not compatible.
EDIT: Using code provided by Prany I am now able to output just the Audio File Name, but it I try and also output the sCallId I get a duplication Issue (see below output):
Getting Calls
2016\03\30\300320161614210106361-00000934412405.asf--84377-3668343241-514513
2016\03\30\300320161614210106361-00000934412405.asf--84385-3668343557-255773
2016\03\30\300320161614210106361-00000934412405.asf--84392-3668344445-516453
2016\03\30\300320161614210106361-00000934412405.asf--85000-3668749568-733799
2016\03\30\300320161614210106361-00000934412405.asf--85604-3668872399-722313
2016\03\30\300320161620220106362-00000934052048.asf--84377-3668343241-514513
2016\03\30\300320161620220106362-00000934052048.asf--84385-3668343557-255773
2016\03\30\300320161620220106362-00000934052048.asf--84392-3668344445-516453
2016\03\30\300320161620220106362-00000934052048.asf--85000-3668749568-733799
2016\03\30\300320161620220106362-00000934052048.asf--85604-3668872399-722313
2016\03\30\300320161634220106363-00000933211384.asf--84377-3668343241-514513
2016\03\30\300320161634220106363-00000933211384.asf--84385-3668343557-255773
2016\03\30\300320161634220106363-00000933211384.asf--84392-3668344445-516453
2016\03\30\300320161634220106363-00000933211384.asf--85000-3668749568-733799
2016\03\30\300320161634220106363-00000933211384.asf--85604-3668872399-722313
2016\04\04\040420160908190106389-00000527974488.asf--84377-3668343241-514513
2016\04\04\040420160908190106389-00000527974488.asf--84385-3668343557-255773
2016\04\04\040420160908190106389-00000527974488.asf--84392-3668344445-516453
2016\04\04\040420160908190106389-00000527974488.asf--85000-3668749568-733799
2016\04\04\040420160908190106389-00000527974488.asf--85604-3668872399-722313
2016\04\05\050420161913220106406-00000405271715.asf--84377-3668343241-514513
2016\04\05\050420161913220106406-00000405271715.asf--84385-3668343557-255773
2016\04\05\050420161913220106406-00000405271715.asf--84392-3668344445-516453
2016\04\05\050420161913220106406-00000405271715.asf--85000-3668749568-733799
2016\04\05\050420161913220106406-00000405271715.asf--85604-3668872399-722313
Below Is the code I am currently using to try and do this.
//Run the SQL and wrap the output in results tags to fix Multiple Root Elements error.
string liveXML = "<results>" + cmd2.ExecuteScalar().ToString() + "</results>";
//Create new XML Document
XmlDocument LiveDoc = new XmlDocument();
LiveDoc.LoadXml(liveXML);
//Conver XML to JSON
sjsonLive = JsonConvert.SerializeXmlNode(LiveDoc);
//Output RAW JSON
txtOut.AppendText("\r\n" + sjsonLive);
//Parse JSON into an Array
var files = JObject.Parse(sjsonLive);
//We want to run this values in a files seach, but for now let's print it to txtOut
foreach (var f in files.SelectTokens("$..calls..#audioFileName"))
foreach (var c in files.SelectTokens("$..calls..#sCallID"))
{
txtOut.AppendText("\r\n" + f.ToString() + " - " + c.ToString());
//Conduct File Search Here...
}
Example JSON Data:
{
"results": {
"calls": [{
"#audioFileName": "2016\\03\\30\\300320161614210106361-00000934412405.asf",
"#sCallID": "84377-3668343241-514513"
}, {
"#audioFileName": "2016\\03\\30\\300320161620220106362-00000934052048.asf",
"#sCallID": "84385-3668343557-255773"
}, {
"#audioFileName": "2016\\03\\30\\300320161634220106363-00000933211384.asf",
"#sCallID": "84392-3668344445-516453"
}, {
"#audioFileName": "2016\\04\\04\\040420160908190106389-00000527974488.asf",
"#sCallID": "85000-3668749568-733799"
}, {
"#audioFileName": "2016\\04\\05\\050420161913220106406-00000405271715.asf",
"#sCallID": "85604-3668872399-722313"
}
]
}
}
Edit:
You can use below token selector
files.SelectTokens("$..calls..#audioFileName")
edit 2:
var calls = files.SelectTokens("$..calls..#sCallID").ToList();
var audiofiles = files.SelectTokens("$..calls..#audioFileName").ToList();
for (int i = 0; i <= audiofiles.Count; i++)
{
//Conduct File search
if (true)
{
//access by index like audiofiles[i] and append to the query
}
else
{
//access calls by index like calls[i] and append to the query
}
}