LINQ to XML - How to get a dictionary from an anonymous object?
I am currently getting the HeaderColumns list from the following XML snippet:
<PerformancePanel>
<HeaderColumns>
<column performanceId="12" text="Over last month %" />
<column performanceId="13" text="Over last 3 months %" />
<column performanceId="16" text="1 Year %" />
<column performanceId="18" text="3 Years % p.a." />
<column performanceId="20" text="5 Years % p.a." />
<column performanceId="22" text="10 Years % p.a." />
</HeaderColumns>
</PerformancePanel>
from which I create an object like this: (validly similar to the earlier question!)
var performancePanels = new
{
Panels = (from panel in doc.Elements("PerformancePanel")
select new
{
HeaderColumns = (from column in panel.Elements("HeaderColumns").Elements("column")
select new
{
PerformanceId = (int)column.Attribute("performanceId"),
Text = (string)column.Attribute("text")
}).ToList(),
}).ToList()
};
I would like the HeaderColumns to be Dictionary (), so later I retrieve the values from the anonymous object like:
Dictionary<int, string> myHeaders = new Dictionary<int, string>();
foreach (var column in performancePanels.Panels[0].HeaderColumns)
{
myHeaders.Add(column.PerformanceId, column.Text);
}
I thought I could achieve this with Linq for XML with something similar to this
HeaderColumns = (from column in panel.Elements("HeaderColumns").Elements("column")
select new Dictionary<int, string>()
{
(int)column.Attribute("performanceId"),
(string)column.Attribute("text")
}).ToDictionary<int,string>(),
but it doesn't work because
- ToDictionary () needs a Func parameter and I don't know what it is / how to implement it, and
- the code is probably wrong anyway!
Can someone suggest me how I can achieve the result I want? Thank you.
Edit: I tried to implement the Spender solution below, but I'm having a hard time figuring out what I need to change in my code. Can anyone clarify? Thanks to
a source to share
I found this to be a suitable answer to this question:
How can I create a dictionary <int, string> via LINQ to XML?
a source to share
This is untested, but should do the trick:
panel
.Elements("HeaderColumns")
.Elements("column")
.ToDictionary(
column=>(int)column.Attribute("performanceId"),
column=>(string)column.Attribute("text")
)
In this case, I am using the following ToDictionary overload :
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector
)
So here it source
will be:
panel
.Elements("HeaderColumns")
.Elements("column")
Both keySelector
and elementSelector
are functions (delegates) that take an element of type TSource (from source
) and return the value obtained from the element:
keySelector
:
column=>(int)column.Attribute("performanceId")
elementSelector
:
column=>(string)column.Attribute("text")
They are declared in lambda format. The dictionary type is then inferred from the return types of the delegates. In this case it will be Dictionary<int,string>
.
The generic delegate Func<TSource, TKey>
will define the type of the method that takes a parameter of type TSource and returns a value of type TKey. It is very important to understand what this means in order to have a productive time using Linq extension methods.
a source to share
It is difficult to give a complete answer as your xml and C # are not the same; but from each "HeaderColumns" you can use:
var columns = from column in headerColumns.Elements("column")
select new {
PerformanceId = (int)column.Attribute("performanceId"),
Text = (string)column.Attribute("text")
};
Dictionary<int, string> myHeaders = columns.ToDictionary(
column => column.PerformanceId, column => column.Text);
If you have several <HeaderColumns>
, then SelectMany
maybe ...
var columns = from panel in doc.Elements("PerformancePanel")
from headers in panel.Elements("HeaderColumns")
from column in headers.Elements("column")
select new {
PerformanceId = (int)column.Attribute("performanceId"),
Text = (string)column.Attribute("text")
};
Dictionary<int, string> myHeaders = columns.ToDictionary(
column => column.PerformanceId, column => column.Text);
a source to share