XML to SelectList using LINQ - c#

I am attempting to get a Select List from an XML string...
<?xml version="1.0" encoding="utf-8"?>
<selectListItemDefinition id="UPMCTypes">
<item key="bla1" value="bla" />
<item key="bla2" value="bla" />
<item key="bla3" value="bla" />
<item key="bla4" value="bla" />
<item key="bla5" value="bla" />
<item key="bla6" value="bla" />
<item key="bla7" value="bla" />
<item key="bla8" value="bla" />
<item key="bla9" value="bla" />
<item key="bla10" value="bla" />
<item key="bla11" value="bla" />
</selectListItemDefinition>
That would be the XML string that I am trying to turn into a SelectList
Here is how I am trying to do it...
List<SelectListItem> SListItem =
(
from xmlSList in xDoc.Descendants("selectListItemDefinition")
let listItem = xmlSList.Elements("item")
select new SelectListItem
{
Key = xmlSList.Attribute("key").Value,
Value = xmlSList.Attribute("value").Value
}
).ToList();
This only gets the first Key-Value one for me.
Key "blah1" string
Value "blah" string
Now I think it's because I am only getting one Element here? But I am not sure what I would do to get this right.

I think you have a problem in your query, as you should be getting an exception.
You are trying to select the attributes "key" and "value" of <selectListItemDefinition>, but they don't exist. And you are not actually using listItem in your select clause.
Your first line is returning an IEnumerable<XElement> of all nodes called "selectListItemDefinition". Your second line is just taking that IEnumerable<> and assigning the child elements called "item" it to variable named listItem.
When you should be doing is a second query to iterate over the <selectListItemDefinition> elements instead of assigning it.
So replace let listItem = xmlSList.Elements("item") with from listItem in xmlSList.Elements("item") and it should behave as you expect.
the full query would be:
var SListItem =
(
from xmlSList in xDoc.Descendants("selectListItemDefinition")
from listItem in xmlSList.Elements("item")
select new SelectListItem
{
Key = listItem .Attribute("key").Value,
Value = listItem .Attribute("value").Value
}
).ToList();
And if you are 100% certain that the only elements called <item> in your XML document are the ones you want, you can replace the first and second lines with a single one from listItem in xDoc.Descendants("item") and you will get the same behavior.

Related

Intersect 2 Xml Files with XDocument in C#

I have 2 XML Files and I want to get all the XNodes , which are in both files, only based on their same Attribute "id".
This is how an XML File looks like:
<parameters>
<item id="57">
<länge>8</länge>
<wert>0</wert>
</item>
<item id="4">
<länge>8</länge>
<wert>0</wert>
</item>
<item id="60">
<länge>8</länge>
<wert>0</wert>
</item>
</parameters>
Given a second XML File which looks like this:
<parameters>
<item id="57">
<länge>16</länge>
<wert>10</wert>
</item>
<item id="144">
<länge>16</länge>
<wert>10</wert>
</item>
</parameters>
Now I only want the XNode with the ID=57, since it is available in both files. So the output should look like this:
<item id="57">
<länge>8</länge>
<wert>0</wert>
</item>
I already did intersect both files like this:
aDoc = XDocument.Load(file);
bDoc = XDocument.Load(tmpFile);
intersectionOfFiles = aDoc.Descendants("item")
.Cast<XNode>()
.Intersect(bDoc.Descendants("item")
.Cast<XNode>(), new XNodeEqualityComparer());
This only seems to work, when all the descendant Nodes are the same. If some value is different, it won't work. But I need to get this to work on the same Attributes, the values or the descendants doesn't matter.
I also tried to get to the Attributes and intersect them, but this didn't work either:
intersectionOfFiles = tmpDoc
.Descendants(XName.Get("item"))
.Attributes()
.ToList()
.Intersect(fileDoc.Descendants(XName.Get("item")).Attributes()).ToList();
Am I missing something or is this a completely wrong approach?
Thanks in advance.
You should create your own IEqualityComparer that compares XML attributes you want:
public class EqualityComparerItem : IEqualityComparer<XElement>
{
public bool Equals(XElement x, XElement y)
{
return x.Attribute("id").Value == y.Attribute("id").Value;
}
public int GetHashCode(XElement obj)
{
return obj.Attribute("id").Value.GetHashCode();
}
}
which you would then pass to XML parsing code:
var intersectionOfFiles = aDoc.Root
.Elements("item")
.Intersect(
bDoc.Root
.Elements("item"), new EqualityComparerItem());
I also changed some parts of your XML parsing code (XElement instead of XNode since "item" is XML element and "id" is XML attribute).

Multi-value XML item to C#

I have a XML file which has a set of items formatted like this:
<MyItems>
<item size_x="2" size_y="2"/>
<item value1="0" value2="0"/>
<item value1="0" value2="0"/>
<item value1="0" value2="0"/>
<item value1="0" value2="0"/>
<MyItems/>
I'm trying to deserialize this into a 2D array, But i don't know what data type the items translate into in C#.
Are these structs? And if so, How can the first entry in the list have different names for it's values?
And would the formatting be the same if i re-serialize the list from my code?
Any help is appreciated.
System.Xml.Linq.XDocument XD = System.Xml.Linq.XDocument.Load("URL");
Dictionary<string,string> Data = (from _Mi in XD.Elements("MyItems")
from _It in _Mi.Elements("item")
select new
{
V1= n.Attribute("value1").Value,
V2= n.Attribute("value2").Value
}).ToDictionary(c => (string)c.V1, c => (string)c.V2);
you can also use
V1 = n.FirstAttribute.Value;
V2 = n.LastAttribute.Value;
or
_values = n.Attributes.tolist();

Parse XML file nodes and store it to a Dictionary

<?xml version="1.0" encoding="utf-8" ?>
<LanguagePacks>
<Language name = "EN">
<item key="play" value="play"/>
<item key="pause" value="pause"/>
<item key="resume" value="resume"/>
<item key="all" value="all"/>
<item key="songs" value="songs"/>
<item key="song" value="song"/>
<item key="skip" value="skip"/>
<item key="next" value="next"/>
<item key="previous" value="previous"/>
<item key="number" value="number"/>
<item key="album" value="album"/>
</Language>
<Language name = "DE">
<item key="play" value="spiel"/>
<item key="pause" value="pause"/>
<item key="resume" value="resume"/>
<item key="all" value="alle"/>
<item key="songs" value="lieder"/>
<item key="song" value="lied"/>
<item key="skip" value="skipp"/>
<item key="next" value="nachste"/>
<item key="previous" value="vorheriger"/>
<item key="number" value="nummer"/>
<item key="album" value="alben"/>
</Language>
</LanguagePacks>
I want to parse the above xml file for Language name == "EN" and store the key value pairs in a dictionary. The code I am trying in below.. But error shows item with same key is already been added. Please help.
XDocument doc = XDocument.Load($"{path}");
var output = doc.Element("LanguagePacks")
.Descendants().Where(r=>(string)r.Attribute("name").Value == "EN")
.Descendants()
.ToDictionary(k => k.Name, v => v.Value);
.ToDictionary(k => k.Name, v => v.Value);
k is an item, if you want your Dictionary like
[0] = {[play, play]}
[1] = {[pause, pause]}
I think for each item, you access the attribute to get value
var output = doc.Element("LanguagePacks")
.Descendants().Where(r=>(string)r.Attribute("name").Value == "EN")
.Descendants() // list of item
.ToDictionary(k => k.Attribute("key").Value, v => k.Attribute("value").Value);
ToDictionary(k => k.Name, v => v.Value) will not work the way you want, because the Name and Value properties are the node name (item) and node contents (nothing), not the xml attributes "key" and "value".
You also should not use Descendants when you actually only want to loop through elements on one level.
Running your code on your example XML gives a NullReferenceException, because some descendants of <LanguagePacks> does not have a name attribute, and you assume they all do.
This code should do what you want. Note that this will also crash if you have duplicate keys, or if an <item /> element is missing the key or value attributes.
doc.Element("LanguagePacks")
.Elements("Language")
.Where(x => x.Attribute("name")?.Value == "EN")
.Elements("item")
.ToDictionary(x => x.Attribute("key").Value, x => x.Attribute("value").Value)
.Dump();
In
.ToDictionary(k => k.Name, v => v.Value);
k.Name will always be item, not the value of key="...".
And I guess that v.Value will always be "", not entirely sure.

Find parent with defined attribute wuth LINQ

I have variable XElement content =
"...
<item text="Name-1" id="1" segment="1" secret-id="Find-Me">
<item text="Name-1-1" id="1.1">
<item text="Name-1-1-1" id="1.1.1" />
<item text="Name-1-1-2" id="1.1.2" />
</item>
</item>
..."
I have also an item with id = 1.1.2
I need to find the first parent of this item which has attribute segment="1" and get its secret-id
How to do this with LINQ?
You mean the "closest ancestor"?
string secretId = element.Ancestors()
.Where(x => (string) x.Attribute("segment") == "1")
.Select(x => (string) x.Attribute("secret-id"))
.FirstOrDefault();
The result will be null if either:
There are no ancestors with an attribute segment="1"
The element with that attribute has no secret-id attribute.
Like this:
item.Ancestors().Select(p => p.Attribute("secret-id")).First(a => a != null)

LINQ to XML Selecting Child Elements

I am trying to extract information from an XML file into an object using LINQ to XML. Although I can return the document and section Id attributes I cannot get access to the Items for each section element, it returns an IEnumerable of all the items in the document. I know this is correct as I’m calling Descendants but am struggling to get it to return only the child items of each section element. Can anybody help?
XML Document
<root>
<document id="1">
<section id="1.1">
<item id="1.1.1"></item>
<item id="1.1.2"></item>
<item id="1.1.3"></item>
</section>
<section id="1.2">
<item id="1.2.1"></item>
<item id="1.2.2"></item>
</section>
</document>
</root>
LINQ Query
XElement documentRoot = XElement.Load("document.xml");
var documents = (from docs in documentRoot.Descendants("document")
select new
{
Id = (string) docs.Attribute("id"),
Sections = docs.Elements("section"),
Items = docs.Elements("section").Elements("item")
}).ToList();
foreach(var doc in documents)
{
foreach(var section in doc.Sections)
{
Console.WriteLine("SectionId: " + section.Attribute("Id"));
foreach(var item in doc.Items)
{
Console.WriteLine("ItemId: " + section.Attribute("Id"));
}
}
}
You got some small typos, on attribute Id and at item loop. But if you're trying to get all section items into that items collection, you're wrong at design level, as Items should be a Section property, not Document (so you'll need to query your XML twice).
Or, you can to do something like:
var documents =
(from docs in documentRoot.Descendants("document")
select new
{
Id = (string) docs.Attribute("id"),
Sections = docs.Elements("section")
}).ToList();
foreach (var doc in documents)
{
foreach (var section in doc.Sections)
{
Console.WriteLine("SectionId: " + section.Attribute("id"));
foreach (var item in section.Elements("item"))
{
Console.WriteLine("ItemId: " + item.Attribute("id"));
}
}
}
Output:
SectionId: id="1.1"
ItemId: id="1.1.1"
ItemId: id="1.1.2"
ItemId: id="1.1.3"
SectionId: id="1.2"
ItemId: id="1.2.1"
ItemId: id="1.2.2"
Do you want a flat structure ?!?! (from LinqPad)
XElement documentRoot = XElement.Parse (
#"<root>
<document id='1'>
<section id='1.1'>
<item id='1.1.1'></item>
<item id='1.1.2'></item>
<item id='1.1.3'></item>
</section>
<section id='1.2'>
<item id='1.2.1'></item>
<item id='1.2.2'></item>
</section>
</document>
</root>
");
var documents = (from docs in documentRoot.Descendants("section")
select new
{
SectionId = (string) docs.Attribute("id"),
Items = docs.Elements("item")
});
documents.Dump();
This gives 2 SectionId items that containes XElements with related items.

Categories

Resources