Column Name of dynamic object - c#

How can I get the header (columns Name) of a dynamic List ?
here is my code
var Participants = Newtonsoft.Json.JsonConvert.DeserializeObject<List<dynamic>>(obj.ToString());
and I need to get the name of all the column inside the var Participants
Any ideas ?

Use Newtonsoft.Json.Linq.JProperty, take for example you have the following json object:
{ "summary" : { "123": {}, "456": {} } }
You want to deserialize to a list of the following:
class SomeClass {
public string ID {get;set;}
}
Your code would be:
dynamic responseDynamic = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
List<SomeClass> someList = new List<SomeClass>();
foreach (var item in responseDynamic.summary)
{
string id = ((Newtonsoft.Json.Linq.JProperty)item).Name;
someList.Add(new SomeClass(){ID = id});
}

Related

How to remove JSON properties with a specific name from an array of objects?

I want to remove JSON properties by a specific "key" but it is not working as I expected. My code hasn't changed a thing.
I did the following
void Start()
{
var foodlist = new List<Food>()
{
new() { name = "Banana", price = 3000 },
new() { name = "Apple", price = 1000}
};
// SerializeObject()
string jasonString = JsonConvert.SerializeObject(foodlist);
JArray jArray = JArray.Parse(jasonString);
// Jarray => String Serialize
string jarrayString2 = JsonConvert.SerializeObject(jArray);
foreach (var jObject in jArray.Children<JObject>())
{
int indexNum = 0;
foreach (var jProperty in jObject.Properties())
{
if(jProperty.Name == "name")
{
jArray.Remove(jArray[indexNum][jProperty.Name]);
indexNum++;
}
}
}
// Check
string jarrayString = JsonConvert.SerializeObject(jArray);
print(jarrayString);
}
public class Food
{
public string name;
public int price;
}
**The result hasn't changed **
Output
[{"name":"Banana","price":3000},{"name":"Apple","price":1000}]
Result that I want
[{"price":3000},{"price":1000}]
So there a couple of issues with your code. First and foremost seems to be a bug where you are counting iterations during the foreach loop of the properties and not during the loop of the objects. i.e. You want to remove the property for each item in the array but the property is always at position 0. So for every loop you always remove the object property called "name" at position 0. This can be illustrated by setting the value to null instead of removing it. You will see that your output has the first object with null name but the following objects will remain unchanged.
I've had an attempt at making it work as is but where I've landed is basically just a for loop that does the same as a foreach would, with extra work.
e.g. We can try to fix your initial code as such:
int indexNum = 0;
foreach (var jObject in jArray.Children<JObject>())
{
foreach (var jProperty in jObject.Properties())
{
if(jProperty.Name == "name")
{
jArray[indexNum][jProperty.Name] = null;
}
}
indexNum++;
}
This should target the correct property in each object, but it could really be simplified down to something like this:
for (int i = 0; i < jArray.Count; i++)
{
var jObj = jArray[i] as JObject;
jObj.Remove("name");
}
And then simplifying that even further we could do it in one simple foreach like this:
foreach (JObject jObj in jArray)
{
jObj.Remove("name");
}
EDIT: I notice that the top foreach method throws an exception that "Collection was modified; enumeration operation may not execute." and so I've just set the value to null instead of try to remove it. But I'll leave the rest of the answer as is for reference.
Just use the JsonIgnore attribute.
Instructs the JsonSerializer not to serialize the public field or public read/write property value. More...
Change:
public class Food
{
public string name;
public int price;
}
...to:
public class Food
{
[JsonIgnore] // <-------- This causes 'name' to be excluded from serialisation
public string name;
public int price;
}
...and use like so:
var foodlist = new List<Food>()
{
new() { name = "Banana", price = 3000 },
new() { name = "Apple", price = 1000}
};
string jasonString = JsonConvert.SerializeObject(foodlist);
See also
JsonIgnoreAttribute, Json.NET
For the sake of information, if your json has useless data, simply ignore them:
{
"name" : "banana",
"price" : 1000
}
[Serializable]
public class Item
{
public int price;
}
void Start()
{
Item itemInstance = JsonUtility.FromJson<Item>(json);
}
in this example Item has no "name".

How to change JSON's value for List (or array) type?

"title" : { "newTitle" : "Test"}
"tags" : { "newTags" : ["Tag1", "Tag2"] }
Newtonsoft.Json.Linq;
var json = JObject.Parse(json: json);
var title; // "Test2" in title
List<string> tags; // "TagA", "TagB", "TagC" in tags
json["title"]["newTitle"] = title; // works well
json["tags"]["newTags"] = tags; // not work
I want the JSON result as below:
"title" : { "newTitle" : "Test2"}
"tags" : { "newTags" : ["TagA", "TagB", "TagC"] }
I want to modify some values of JSON. int or string worked well. However, the List or Array does not work.
Please give me a good opinion.
I used a translator. So the writing may be awkward.
Assume your JSON data has defined object template, you can create a class based on your JSON data. With Newtonsoft.Json, you deserialize your JSON into an object and next update the object's properties value.
Note: When access object's inner properties for example Title.NewTitle and Tags.NewTags, you may need to add some null checking for preventing NullReferenceException.
1st solution: Convert to strongly-typed object
public static void Main()
{
var json = "{\"title\" : { \"newTitle\" : \"Test\"}, \"tags\" : { \"newTags\" : [\"Tag1\", \"Tag2\"] }}";
var inputObj = JsonConvert.DeserializeObject<JsonInput>(json);
inputObj.Title.NewTitle = "Test2";
inputObj.Tags.NewTags = new List<string> {"TagA", "TagB", "TagC"};
Console.WriteLine(JsonConvert.SerializeObject(inputObj));
}
public class JsonInput
{
public Title Title {get;set;}
public Tags Tags {get;set;}
}
public class Title
{
public string NewTitle {get;set;}
}
public class Tags
{
public List<string> NewTags {get;set;}
}
1st solution Code snippets and Output
2nd solution: With dynamic
To update array, you need parse your List<string> to JArray type
public static void Main()
{
var json = "{\"title\" : { \"newTitle\" : \"Test\"}, \"tags\" : { \"newTags\" : [\"Tag1\", \"Tag2\"] }}";
var title = "Test2"; // "Test2" in title
List<string> tags = new List<string> {"TagA", "TagB", "TagC"}; // "TagA", "TagB", "TagC" in tags
dynamic root = JObject.Parse(json);
JObject titleObj = (JObject)root["title"];
titleObj["newTitle"] = title;
JObject tagsObj = (JObject)root["tags"];
tagsObj["newTags"] = JArray.FromObject(tags);
Console.WriteLine(root);
}
2nd solution Code snippets and Output
try this
var jsonObject=JObject.Parse(json);
var newTitle = "Test2";
List<string> newTags = new List<string> { "TagA", "TagB", "TagC"};
jsonObject["title"]["newTitle"]= newTitle;
jsonObject["tags"]["newTags"]= JArray.FromObject(newTags);
result
{
"title": {
"newTitle": "Test2"
},
"tags": {
"newTags": [
"TagA",
"TagB",
"TagC"
]
}
}

what is the best way to write a list of items in a text file in tree view form

i have a list of objects , and each object have a list of dependant objects , i want to write the list in a text file in a tree view form .
i tried doing foreach on the list but i can't all dependencies and the correct levels of objects
//the list of all objects
List<Object> objects;
//object Class
class Object {
string name;
List<Object> depandantObj;
}
the expected result must be writen in text file under the form :
object1:
object2
object3:
object5
object1
object6:
object2
etc...
Recursive method to append a new line for each object with indent:
public string GetText(Object obj, int indentLevel)
{
string text = "";
string indentation = new string(' ', indentLevel * 8);
text += indentation + obj.name;
if (obj.depandantObj != null && obj.depandantObj.Count > 0)
{
indentLevel++;
foreach (Object o in obj.depandantObj)
{
text += Environment.NewLine + GetText(o, indentLevel);
}
}
else
return text;
return text;
}
Call the method for each object in the list and write the text into the text file at the end:
make sure both of the fields (name and depandantObj) in Object class are public
List<Object> objects;
//add items to list
...
if(objects != null)
{
string text = "";
foreach (Object obj in objects)
{
text += GetText(obj, 0);
}
File.WriteAllText(Server.MapPath("~/sample.txt"), text);
}
First create your Objects with nested list forms. For example:
public class MyObject{
// some properties here
}
public class MySecondObject{
List<MyObject> {get; set;}
}
public class MythirdObject{
List<MySecondObject> {get; set;}
}
And when you want to save the data to a file just seriliaze them to json , it will already create a readable json file for you.
// I assume you can create your data or fetch them
var data = List<MyThirdObject> ();
string json = JsonConvert.SerializeObject(_data);
//write string to file
System.IO.File.WriteAllText(#"D:\path.txt", json);
If you don't want json than you can create a recusrive method that add each object to under last one.
Have a look this question for how you can this at this way.
Consider using json, download newtonsoft dll from Nuget.
a code example:
public class MyObject
{
public string Name { get; set; }
}
public class MySecondObject
{
public List<MyObject> DepObj { get; set; } = new List<MyObject>();
}
usage example:
MyObject obj = new MyObject
{
Name = "example"
};
MySecondObject mySecond = new MySecondObject();
mySecond.DepObj.Add(obj);
var data = new List<MySecondObject>
{
mySecond
};
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
System.IO.File.WriteAllText(#"D:\file.txt", json);
File content:
[
{
"DepObj": [
{
"Name": "example"
}
]
}
]
First, let's elaborate initial Object class; I've renamed it (in order not to conflict with System.Object), make fields being public, add a constructor:
class MyObject {
public string name = "";
public List<MyObject> depandantObj = new List<MyObject>();
public MyObject(string value, params MyObject[] dependent) {
name = value;
if (dependent != null)
foreach (var item in dependent)
depandantObj.Add(item);
}
}
Then we can implement an iterator, IEnumerable<string>:
private static IEnumerable<string> MyObjectToTree(IEnumerable<MyObject> roots, int shift = 6) {
if (null == roots)
yield break;
foreach (var root in roots) {
// We don't want infinte loop if objects create a cycle
HashSet<MyObject> completed = new HashSet<MyObject>();
Stack<Tuple<int, MyObject>> agenda = new Stack<Tuple<int, MyObject>>();
agenda.Push(Tuple.Create(0, root));
while (agenda.Any()) {
Tuple<int, MyObject> item = agenda.Pop();
if (!completed.Add(item.Item2))
continue;
List<MyObject> children = item.Item2?.depandantObj ?? new List<MyObject>();
children.Reverse();
yield return $"{new string(' ', shift * item.Item1)}{item.Item2?.name}{(children.Any() ? ":" : "")}";
foreach (var child in children)
agenda.Push(Tuple.Create(item.Item1 + 1, child));
}
}
}
Demo:
// I've added the MyObject constructor for this readable creation
List<MyObject> objects = new List<MyObject>() {
new MyObject("object1",
new MyObject("object2"),
new MyObject("object3",
new MyObject("object4"),
new MyObject("object5"))),
new MyObject("object6",
new MyObject("object2")),
};
foreach (string line in MyObjectToTree(objects, 6))
Console.WriteLine(line);
// If you want to write into file:
// File.WriteAllLines(#"c:\MyFile.txt", MyObjectToTree(objects, 6));
Outcome:
object1:
object2
object3:
object4
object5
object6:
object2

How to get value from IEnumerable collection using its Key?

I have IEnumerable collection like following
IEnumerable<Customer> items = new Customer[]
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
I want to get value using key Id
I tried like following
public int GetValue(IEnumerable<T> items,string propertyName)
{
for (int i = 0; i < items.Count(); i++)
{
(typeof(T).GetType().GetProperty(propertyName).GetValue(typeof(T), null));
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
}
}
If you want to retrieve a Customer name from a collection by its Id:
public string GetCustomerName(IEnumerable<Customer> customers, int id)
{
return customers.First(c => c.Id == id).Name;
}
Using LINQ you can get all customers names (values) having specific value in this way:
var valuesList = items.Where(x => x.Something == myVar).Select(v => v.Name).ToList();
For single customer name you can do this:
var singleName = items.FirstOrDefault(x => x.Id == 1)?.Name;
Obviously, the Id can be 1, 2 or any other.
Edit:
I recommend you List<Customer> instead of Customer[]
So,
var items = new List<Customer>
{
new Customer { Name = "test1", Id = 999 },
new Customer { Name = "test2", Id = 989 }
};
// I will pass propertyName as Id and want all Id propperty values
// from items collection one by one.
If I understand you correctly
public static IEnumerable<object> GetValues<T>(IEnumerable<T> items, string propertyName)
{
Type type = typeof(T);
var prop = type.GetProperty(propertyName);
foreach (var item in items)
yield return prop.GetValue(item, null);
}
Just use LINQ to achieve what you want to do. if you want to retrieve a specific value you can use where like this:
public Customer GetCustomerById(IEnumerable<Customer> items,int key)
{
return items.Where(x=>x.id==key)
.Select(x =>x.Name)
.First();
}
this will retrieve the customer who match a specific Id.
Do you want to look things up repeatedly after creating the list? If so, you might want to consider creating a dictionary to do the lookups, like so:
IEnumerable<Customer> items = new Customer[]
{
new Customer {Name = "test1", Id = 999},
new Customer {Name = "test2", Id = 989}
};
var lookup = items.ToDictionary(itemKeySelector => itemKeySelector.Id);
var result = lookup[989];
Console.WriteLine(result.Name); // Prints "test2".
I'm assuming that you don't create the collection in the first place - if you had control over creating the original collection you could use a dictionary in the first place.
private TextBox [] Collectionstextboxonpanel(Panel panel)
{
var textBoxspanel1 = panel.Controls.OfType<TextBox>(); // select controls on panle1 by type
IEnumerable<TextBox> textBoxes = textBoxspanel1; // create collection if need
TextBox[] textBoxes1 = textBoxes.ToArray(); // Array collection
return textBoxes1; // get back TextBox Collection
}

Issue displaying LINQ query results in a grid in C#

I received some help here with the following LINQ query, but am still struggling with it. The result I'm trying to obtain is to display some attributes and their values from an xml file in a DataGridView control. I'm calling my method from a button click and am trying to pass back the list for display in the grid. Here is an example of the row:
<z:row CenterCode="JAX" CenterName="Jacksonville" Version="1.0" NextExport="66742" NextImport="29756" LastImportTime="2015-06-10T14:48:33" FtpProxyServer="" FtpUserName="" FtpPassword="" ResetImportID="False"/>
Here is the method:
public static List<string[]> MonitorCounts(string upperLimit)
{
// Load xml
XDocument xmldoc = XDocument.Load(#"c:\XML\Configuration.xml");
XNamespace z = "#RowsetSchema";
Int32 limit = Convert.ToInt32(upperLimit);
var elementQuery = xmldoc.Descendants(z + "row").Where(e => (long?)e.Attribute("NextExport") > limit | (long?)e.Attribute("NextImport") > limit);
var attributes = elementQuery.Select(e => e.Attributes().Select(a => new KeyValuePair<string, string>(a.Name.LocalName, (string)a)).ToList()).ToList();
return attributes;
}
My questions are how to select only specific attributes and values in attributes. If I do something like this:
var attributes = elementQuery.Select(e => e.Attributes("CenterName").Select(a => new KeyValuePair<string, string>(a.Name.LocalName, (string)a)).ToList()).ToList();
then this is returned:
[0] = {[CenterName, Jacksonville]}
I need to select this and 4 others. I'm also getting a convrsion error - Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string,string>>>' to 'System.Collections.Generic.List<string[]>. Appreciate any pointers to help me along.
You can use an anonymous type:
var attributes =
elementQuery.Select(e => new
{
CenterName = (string)e.Attribute["CenterName"],
Version = (string)e.Attribute["Version"],
// more attributes
}).ToList();
You can't however return this from the method in a useful way. So if you really need both the attribute name and the attribute value as strings, try this approach instead:
var attributes =
elementQuery.Select(e => new []
{
Tuple.Create("CenterName", (string)e.Attribute["CenterName"]),
Tuple.Create("Version", (string)e.Attribute["Version"]),
// more attributes
}).SelectMany(x => x).ToList();
The return type of your method now has to be List<Tuple<string, string>>.
And finally, if you actually need a List<string[]> as the return type, use this code:
var attributes =
elementQuery.Select(e => new []
{
new [] { "CenterName", (string)e.Attribute["CenterName"] },
new [] { "Version", (string)e.Attribute["Version"] },
// more attributes
}).SelectMany(x => x).ToList();
I solved my own problem. Here is what I did:
Created a class for the attributes needed:
public class dataRow
{
public string CenterName { get; set; }
public string CenterCode { get; set; }
public string NextImport { get; set; }
public string NextExport { get; set; }
public string LastImportTime { get; set; }
}
Selected the results into it:
List<dataRow> dataRows = elementQuery.Select( e => new dataRow
{ CenterName = (string)e.Attribute("CenterName"),
CenterCode = (string)e.Attribute("CenterCode"),
NextImport = (string)e.Attribute("NextImport"),
NextExport = (string)e.Attribute("NextExport"),
LastImportTime = (string)e.Attribute("LastImportTime") }).ToList();
Changed my method to return the correct object:
public static List<dataRow> MonitorCounts(string upperLimit)
Set my grids datasource to the method return:
dataGridView1.DataSource = xmlProcessing.MonitorCounts(tbxUpperLimit.Text.ToString());
return dataRows;

Categories

Resources