How can I add XElement iterating elements? - c#

This is my code:
var xml = new XElement("test", new[] {
new XElement("group", new[] {
new XElement("date", dateNow.ToString("dd/MM/yyyy HH:mm:ss"))
}),
new XElement("users", new[] {
foreach(var item in in PlaceHolderCustom.Controls)
{
new XElement("nome", ((TextBox)item.FindControl("myTextBox")).Text)
}
})
});
I'd like to set in the xml some fixed fields (within the element "group") and some that would iterate across a placeholder. But the syntax seems to be wrong when I try to add a new "iterating" list.
Where am I wrong?

Use linq .Select to perform the foreach. Also when you create the array the new [] {} syntax is valid only for new string[]. In your case use:
new XElement[] {}
Or because the method gets a params object[] you can just give each new XElement independently without wrapping with an array
So showing both ways of passing the collection of XElements:
var xml = new XElement("test",
new XElement("group", new XElement[] {
new XElement("date", dateNow.ToString("dd/MM/yyyy HH:mm:ss"))
}),
new XElement("users", PlaceHolderCustom.Control.Select(item =>
new XElement("nome", ((TextBox)item.FindControl("myTextBox")).Text)).ToArray())
);

Related

How to execute a LINQ query while constructing an XDocument?

I want to create a table that list the count of each file type. I've created a query to get that data. When I create the XDocument, how do I execute the query and create rows in the table with data from query?
var query = listFiles.GroupBy(f => Path.GetExtension(f).ToLower())
.Select(g => new
{
Extension = g.Key,
Count = g.Count(),
});
var doc = new XDocument(
new XElement("html",
new XElement("body",
new XElement("table", new XAttribute("border", 2),
foreach (var f in query)
{
new XElement("tr",
new XElement("td", f.Extension),
new XElement("td", f.Count));
}))));
Try following :
var doc = new XDocument(
new XElement("html",
new XElement("body",
new XElement("table", new object[] {
new XAttribute("border", 2),
query.Select(f =>
new XElement("tr",
new XElement("td", f.Extension),
new XElement("td", f.Count)))
}))));

How to convert list to expandobject key in c#?

I have a function in which i am getting data(array of objects) from db and then adding those objects of array one by one into a lit of type ExpandoObject
public async Task<List<ExpandoObject>> GetGroupWithMaxTickets(){
List<ExpandoObject> topGroupsWithMaxTickets = new List<ExpandoObject>();
dynamic ticketDetails = new ExpandoObject();
var pipeline_tickets = new BsonDocument[]{
new BsonDocument("$match",
new BsonDocument
{
{ "nsp", "/sbtjapan.com" },
{ "datetime",
new BsonDocument
{
{ "$gte", "2019-12-03T00:00:34.417Z" },
{ "$lte", "2019-12-03T24:00:34.417Z" }
} }
}),
new BsonDocument("$group",
new BsonDocument
{
{ "_id", "$group" },
{ "totalTIckets",
new BsonDocument("$sum", 1) }
}),
new BsonDocument("$project",
new BsonDocument
{
{ "_id", 0 },
{ "group", "$_id" },
{ "totalTIckets", 1 }
}),
new BsonDocument("$sort",
new BsonDocument("totalTIckets", -1)),
new BsonDocument("$limit", 5)
};
var collection = await DbService.tickets.AggregateAsync<RawBsonDocument>(pipeline_tickets, new AggregateOptions {UseCursor = true, BatchSize = 500});
await collection.MoveNextAsync();
if(collection.Current.ToList().Count > 0){
// ticketDetails = JsonConvert.DeserializeObject(collection.Current.ToJson());
// ticketDetails.group = collection.Current.ToList()[0]["group"];
// ticketDetails.totalTickets = collection.Current.ToList()[0]["totalTIckets"];
Parallel.ForEach(collection.Current.ToList(), (ticket) => {
Console.WriteLine("Ticket----"+ticket);
dynamic groupWithTickets = new ExpandoObject();
groupWithTickets = ticket;
topGroupsWithMaxTickets.Add(groupWithTickets);
});
}
return topGroupsWithMaxTickets;
}
But it throws an error like this
System.AggregateException: One or more errors occurred. (The best overloaded method match for 'System.Collections.Generic.List<System.Dynamic.ExpandoObject>.Add(System.Dynamic.ExpandoObject)' has some invalid arguments)
I want that my function must return array of objects of type List<ExpandoObject>
How can i do this in c#?
Since you have changed the question, following is the answer that should resolve your matters.
How to NOT work with ExpandoObjects
I tested this on my system and got it to reproduce the same results as you are getting. Following is the failed try:
dynamic employee = new ExpandoObject();
List<ExpandoObject> listOfEmployees = new List<ExpandoObject>();
employee = "someStrangeString";
listOfEmployees.Add(employee); // ERROR !!!!
and just as expected, i get the following error on Add.
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
HResult=0x80131500
Message=The best overloaded method match for 'System.Collections.Generic.List.Add(System.Dynamic.ExpandoObject)' has some invalid arguments
Source=
StackTrace:
Corrected way of ExpandoObject use
Following is the method that will take care of the issues with Adding it to the list.
Parallel.ForEach(collection.Current.ToList(), (ticket) =>
{
Console.WriteLine("Ticket----" + ticket);
dynamic groupWithTickets = new ExpandoObject();
groupWithTickets.users = ticket; //<---- Assign ticket to users element.
topGroupsWithMaxTickets.Add(groupWithTickets);
});
What was done to fix it?
When you are working with ExpandoObjects, you have to think of dictionary type of a deal. When you declare ExpandoObject, you have to dynamically assign the value to an element (that you define).
Example from MS site: shows the proper use of ExpandoObject
dynamic employee, manager;
employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
manager = new ExpandoObject();
manager.Name = "Allison Brown";
manager.Age = 42;
manager.TeamSize = 10;
Hopefully this resolves your issue.
It should be like this;
dynamic ticketDetails = new ExpandoObject();
ticketDetails.user = collection;
string json = Newtonsoft.Json.JsonConvert.SerializeObject(ticketDetails);
You can simply do this:
dynamic ticketDetails = new ExpandoObject();
ticketDetails = Json(new users = JsonConvert.DeserializeObject(collection.Current.ToJson()));
For testing purposes, i used an array arr that holds one of the elements. If you need that array to be part of ExtendoObject with first element being users, you can create a Json object and set the array as value to the "users" element.
dynamic ticketDetails = new ExpandoObject();
JArray arr = new JArray();
arr.Add(#"[{""name"": ""Alex"", ""age"": 21}]");
JObject o = new JObject();
o["users"] = arr.ToString();
ticketDetails = o;
// output: { "users" : [{"name" : "Alex", "age" : 21}]}

How to dynamically populate XElement (linq to xml)

Is there a way I can dynamically add a new XElement to form child nodes as in the example below?
XElement xEl = new XElement(
new XElement("Root",
// ** Is there a way I can do this:
// for(MyObject mObj in myObjects) {
// if (IsXmlObj(mObj)){
// new XElement(mObj.Name, mObj.Value);
// }
// }
);
I would like to iterate through an object list to form the sub nodes.
What if I now modify the iterating part to become:
// for(MyObject mObj in myObjects) {
// if (IsXmlObj(mObj)){
// if (mObject.Name=="Small"){ mObject.Name="Big";}
// new XElement(mObj.Name, mObj.Value);
// }
// }
Use a Select this way:
var xEl = new XElement("Root",myObjects.Where(mObj=>IsXmlObj(mObj))
.Select(mObj=> new XElement(mObj.Name, mObj.Value)));

How to add XElements dynamically in a loop?

I get an HybridDictionary with all the child XElements.
I don't know ahead how many items I have there.
So instead of doing this:
xmlDoc.Element("Parent").Add(
new XElement("Child", new XAttribute("Name", "Child1"),
new XElement("Id", "796"),
new XElement("Name", "gdsa")
etc..
));
I'm trying to do something like that:
Strung [] allKeys = new String[ChildElements.Count];
TheHybridDictionary.Keys.CopyTo(allKeys, 0);
xmlDoc.Element("Parent").Add(
new XElement("Child", new XAttribute("Name", "Child1"),
for (int i = 0; i < TheHybridDictionary.Count; i++)
new XElement(allKeys[i], TheHybridDictionary[allKeys[i]])
But how to connect whatever is inside the for loop to be part of the XML document construction?
Problem is, your HybridDictionary class does not implement IEnumerable, so you can't use LINQ on it directly.
But you can use allKeys string array instead:
string [] allKeys = new String[ChildElements.Count];
TheHybridDictionary.Keys.CopyTo(allKeys, 0);
xmlDoc.Element("Parent").Add(
new XElement("Child", new XAttribute("Name", "Child1"),
allKeys.Select(x => new XElement(x, TheHybridDictionary[x])))
You can use .Add() method as in Stefan's comment, or use LINQ:
xmlDoc.Element("Parent").Add(
new XElement("Child", new XAttribute("Name", "Child1"),
TheHybridDictionary.Select(kvp => new XElement(kvp.Key, kvp.Value)));

How to add another top level node

I have my linq code formatted like:
<Deck>
<Treasure>
<card>
.....
</card>
......
</treasure>
<Door>
<card>
.....
</card>
......
</Door>
In the following code how do I add another Door that is the same "level" as treasure? Everything I have tried keeps adding it as the same level as card. Here is what I have:
public void SaveXml(string path)
{
XElement xml;
XElement root = new XElement("Treasure");
foreach (var item in TreasureCards)
{
xml = new XElement("Card",
new XAttribute("name", item.Name),
new XElement("Type", item.Type),
new XElement("Image",
new XAttribute("path", item.Image)),
new XElement("Usage", item.Usage),
new XElement("Quantity", item.Quantity),
new XElement("Sell", item.Sell)
);
root.Add(xml);
}
root.Add(new XElement("Door"));
foreach (var item in DoorCards)
{
xml = new XElement("Card",
new XAttribute("name", item.Name),
new XElement("Type", item.Type),
new XElement("Image",
new XAttribute("path", item.Image)),
new XElement("Usage", item.Usage),
new XElement("Quantity", item.Quantity));
root.Add(xml);
}
You need to create the Deck element first:
XElement deck = new XElement("Deck");
Then add both the treasure (which i've taken the liberty of renaming from root to treasure) and the door to it:
XElement treasure = new XElement("Treasure")
...
deck.Add(treasure)
...
XElement door = new XElement("Door")
...
deck.Add(door)

Categories

Resources