Getting attributes from same-named XDocument elements using LINQ - c#

I've been coding a program that stores employee data using XDocument:
<!-- School Employee Data -->
<SchoolData storeName="mikveIsrael" location="mikve">
<employee id="1">
<personalInfo>
<name>Ilan Berlinbluv</name>
<zip>58505</zip>
</personalInfo>
<employeeInfo>
<salary>5000</salary>
<id>1</id>
</employeeInfo>
</employee>
<employee id="2">...</employee>
</SchoolData>
I want my program to read every employee id attrib, but I don't know how to do so. Instead, I tried doing this:
var ids = from idz in doc.Descendants("SchoolData")
select new
{
id1 = idz.Element("employee").Attribute("id").Value
};
where doc is the XDocument var. It returns just the first one, but I want it to return an array or List<string>, I just don't know how to iterate through all the same-named employee elements.

XDocument doc = XDocument.Parse(xml);
List<string> ids = doc.Descendants("employee")
.Select(e => e.Attribute("id").Value)
.ToList();

This may helps:
var xDoc = XDocument.Load(path);
var result = xDoc.Descendants("employee")
.SelectMany(i => i.Attribute("id").Value)
.ToList();

Related

Extract data from xml string

Let's say I have an xml string:
<?xml version="1.0" encoding="UTF-8"?>
<Return version="1.0">
<File>1</File>
<URL>2</URL>
<SourceUUID>1191CF90-5A32-4D29-9F90-24B2EXXXXXX0</SourceUUID>
</Return>
and I want to extract the value of SourceUUID, how?
I tried:
XDocument doc = XDocument.Parse(xmlString);
foreach (XElement element in doc.Descendants("SourceUUID"))
{
Console.WriteLine(element);
}
If all you want is the content of the SourceUUID element, and there's only going to be 1 in the XML, you can do this:
XDocument doc = XDocument.Parse(xmlString);
var value = doc.Descendants("SourceUUID").SingleOrDefault()?.Value;
If there are going to be more than one, you can do this:
var values = doc.Descendants("SourceUUID").Select(x => x.Value);
This gives you an enumerable of strings that are the text values of the elements.

Matching XElements in an XDocument against a list of names

I have loaded Xml similar to the following into an XDocument
<requestlist>
<request name="Apple" jobtype="radio">
<schedule intervalminutes="1440" daily="true" updateDateTime="2014-08-07T15:43:00Z">
<channel>someData/channel>
<signal>someData</readysignal>
<frequency>someData</frequency>
</schedule>
<file>someData</file>
<file>someData</file>
</request>
</requestlist>
I now want to add a second request that has some info from he first and deletes some unneeded elements e.g.
<request name="newBanana">
<schedule intervalminutes="1440" daily="true" updateDateTime="2014-08-07T15:43:00Z">
</schedule>
<file>someData</file>
</request>
Is there a simple way I can remove the elements using Linq by matching against a list
e.g.
using System.Xml.XPath;
XDocument configXml = XDocument.Parse(theXml)
var oldRequest = configXml.XPathSelectElement(#"/requestlist/request[#name=""Apple""]");
var nodesToRemove = new List<XName>() {"channel", "signal", "radio"};
var newRequest = oldRequest.Elements().Select(e => e.Name).Intersect(nodesToRemove); //OR something
First make your XPath stops at <schedule> element instead of <request> because you'll be removing child of <schedule> here :
var oldRequest = doc.XPathSelectElement(#"/requestlist/request[#name='Apple']/schedule");
Then yes, you can use simple LINQ to remove elements matched a name in list :
var nodesToRemove = new List<XName>() { "channel", "signal", "radio" };
oldRequest.Elements()
.Where(o => nodesToRemove.Contains(o.Name))
.Remove();

How do I parse text from complex type xml in c# using XDocument?

<?xml version="1.0" encoding="UTF-8"?>
<meta>
<field type="xs-string" name="AssetId">TF00000002</field>
<field type="xs-string" name="Title">TitleOfAsset</field>
</meta>
I have this XML loaded in to a XDocument using the function
XDocument doc = XDocument.Parse(xmlData)
However, I want to be able to retrieve the text fields "TF00000002" and "TitleOfAsset" ... How do I go about doing this?
templateMetaData.assetID = doc
.Descendants()
.Where(p => p.Name.LocalName == "AssetId")
.ToString();
returns:
System.Linq.Enumerable+WhereEnumerableIterator`1[System.Xml.Linq.XElement]
Can anyone shine a light on this?
In your query, you are calling ToString on an IEnumerable<XElement> which will never give you the expected result, instead look for field elements under your Root and get their value:
var values = doc.Root
.Elements("field")
.Select(element => (string)element);
If you want to access your values using the name attribute you can use Dictionary:
var values = doc.Root
.Elements("field")
.ToDictionary(x => (string)x.Attribute("name"), x => (string)x);
Then you can access the value of AssetId:
var id = values["AssetId"];

Override values in one Xml file from another

Looking for a way to merge to XML files where the modified attributes in the second file should override the values of the objects in the first file. Seems like this should be doable with linq to xml but having some trouble figuring out how to do it.
For example take the following two XML files:
File 1:
<root>
<foo name="1">
<val1>hello</val1>
<val2>world</val2>
</foo>
<foo name="2">
<val1>bye</val1>
</foo>
</root>
File 2:
<root>
<foo name="1">
<val2>friend</val2>
</foo>
</root>
The desired end result would be to merge File 2 in to File 1 and end up with
<root>
<foo name="1">
<val1>hello</val1>
<val2>friend</val2>
</foo>
<foo name="2">
<val1>bye</val1>
</foo>
</root>
Sub 'foo' elements should be uniquely identified by their 'name' value with any set values in File 2 overriding the values in File 1.
Any pointers in the right direction would be much appreciated, thanks!
You can just iterate and update values - don't know how generic you want this to be though...
class Program
{
const string file1 = #"<root><foo name=""1""><val1>hello</val1><val2>world</val2></foo><foo name=""2""><val1>bye</val1></foo></root>";
const string file2 = #"<root><foo name=""1""><val2>friend</val2></foo></root>";
static void Main(string[] args)
{
XDocument document1 = XDocument.Parse(file1);
XDocument document2 = XDocument.Parse(file2);
foreach (XElement foo in document2.Descendants("foo"))
{
foreach (XElement val in foo.Elements())
{
XElement elementToUpdate = (from fooElement in document1.Descendants("foo")
from valElement in fooElement.Elements()
where fooElement.Attribute("name").Value == foo.Attribute("name").Value &&
valElement.Name == val.Name
select valElement).FirstOrDefault();
if (elementToUpdate != null)
elementToUpdate.Value = val.Value;
}
}
Console.WriteLine(document1.ToString());
Console.ReadLine();
}
}
You can build new xml from these two:
XDocument xdoc1 = XDocument.Load("file1.xml");
XDocument xdoc2 = XDocument.Load("file2.xml");
XElement root =
new XElement("root",
from f in xdoc2.Descendants("foo").Concat(xdoc1.Descendants("foo"))
group f by (int)f.Attribute("name") into foos
select new XElement("foo",
new XAttribute("name", foos.Key),
foos.Elements().GroupBy(v => v.Name.LocalName)
.OrderBy(g => g.Key)
.Select(g => g.First())));
root.Save("file1.xml");
Thus foo elements from second file selected first, they will have priority over foo elements from first file (when we do grouping).

How can I get all the nodes of a xml file?

Let's say I have this XML file:
<Names>
<Name>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
</Name>
<Name>
<FirstName>James</FirstName>
<LastName>White</LastName>
</Name>
</Names>
And now I want to print all the names of the node:
Names
Name
FirstName
LastName
I managed to get the all in a XmlNodeList, but I dont know how SelectNodes works.
XmlNodeList xnList = xml.SelectNodes(/*What goes here*/);
I want to select all nodes, and then do a foreach of xnList (Using the .Value property I assume).
Is this the correct approach? How can I use the selectNodes to select all the nodes?
Ensuring you have LINQ and LINQ to XML in scope:
using System.Linq;
using System.Xml.Linq;
If you load them into an XDocument:
var doc = XDocument.Parse(xml); // if from string
var doc = XDocument.Load(xmlFile); // if from file
You can do something like:
doc.Descendants().Select(n => n.Name).Distinct()
This will give you a collection of all distinct XNames of elements in the document. If you don't care about XML namespaces, you can change that to:
doc.Descendants().Select(n => n.Name.LocalName).Distinct()
which will give you a collection of all distinct element names as strings.
There are several ways of doing it.
With XDocument and LINQ-XML
foreach(var name in doc.Root.DescendantNodes().OfType<XElement>().Select(x => x.Name).Distinct())
{
Console.WriteLine(name);
}
If you are using C# 3.0 or above, you can do this
var data = XElement.Load("c:/test.xml"); // change this to reflect location of your xml file
var allElementNames =
(from e in in data.Descendants()
select e.Name).Distinct();
Add
using System.Xml.Linq;
Then you can do
var element = XElement.Parse({Your xml string});
Console.Write(element.Descendants("Name").Select(el => string.Format("{0} {1}", el.Element("FirstName").Value, el.Element("LastName").Value)));

Categories

Resources