Read value of specific child node using LINQ - c#

I have a small question. My XML document looks like this:
<?xml version="1.0" encoding="utf-8"?>
<BACKUP>
<HEAD>
<NAME>Test</NAME>
<DATE>19.05.2015 17:05:42</DATE>
</HEAD>
<DIRECTORYSRC>
<SOURCEDIR>C:\Users\User\Pictures</SOURCEDIR>
</DIRECTORYSRC>
<DIRECTORYTRG>
<TARGETDIR>D:\_backup</TARGETDIR>
</DIRECTORYTRG>
</BACKUP>
What is the best way to read the content of NAME and TARGETDIR value? Is there a simple way to read only these specific nodes?
And what if I have more then one TARGETDIR node? Can I do this by using a foreach loop?

You can easily do this using LINQ to XML:
var doc = XDocument.Parse(xml);
var name = (string)doc.Descendants("NAME").Single();
var targetDirs = doc.Descendants("TARGETDIR").Select(e => e.Value).ToList();
name contains your single NAME value, and targetDirs is a List<string> containing all TARGETDIR values.

Related

Performant way to get single element from XML - C#

I got XML code like this:
<Body>
<Schoolyear>2016</Schoolyear>
<ClassLeader>
<Id>200555</Id>
<Name>Martin</Name>
<Short>ma</Short>
</ClassLeader>
<Info>
some very useful information :)
</Info>
</Body>
I only need one tag, e. g. SchoolYear
I tried this:
foreach (XElement element in Document.Descendants("Schoolyear"))
{
myDestinationVariable = element.Value;
}
It works, but I think maybe there is a more performant and easier solution.
You can take it using LINQ or just use Element with the specified XName
Add namespace
using System.Xml.Linq;
And use one of these examples
string xml = #"<Body>
<Schoolyear>2016</Schoolyear>
<ClassLeader>
<Id>200555</Id>
<Name>Martin</Name>
<Short>ma</Short>
</ClassLeader>
<Info>
some very useful information :)
</Info>
</Body>";
XDocument dox = XDocument.Parse(xml);
var exampl1 = dox.Element("Body").Element("Schoolyear").Value;
var exampl2 = dox.Descendants().FirstOrDefault(d => d.Name == "Schoolyear").Value;

Retrieving specific data from XML file

Using LINQ to XML.
I have an XML file which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<TileMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Title>title</Title>
<Abstract>Some clever text about this.</Abstract>
<SRS>OSGEO:41001</SRS>
<Profile>global-mercator or something</Profile>
</TileMap>
I can retrieve the <Title> from this with no problems by using this little piece of code:
string xmlString = AppDomain.CurrentDomain.BaseDirectory + #"Capabilities\" + name + ".xml";
string xmlText = File.ReadAllText(xmlString);
byte[] buffer = Encoding.UTF8.GetBytes(xmlText);
XElement element = XElement.Load(xmlString);
IEnumerable<XElement> title =
from el in element.Elements("Title")
select el;
foreach (XElement el in title)
{
var elementValue = el.Value;
}
However, this isn't very flexible because say I have an XML file that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RootObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Services>
<TileMapService>
<Title>title</Title>
<href>http://localhost/root</href>
</TileMapService>
</Services>
</RootObject>
It can't find <Title> but it finds <Services> (I presume) but since it's not called "Title" it just ignores it. I'm not very strong in working with XML. How would I go about making a method that looks through the XML and fetches me "Title" or however you'd implement this?
You're currently just looking at the child elements of the root element.
Instead, if you want to find all descendants, use Descendants.
Additionally, there's no point in using a query expression of from x in y select x (or rather, there's a very limited point in some cases, but not here). So just use:
var titles = element.Descendants("Title");
Personally I would actually use XDocument here rather than XElement - you have after all got a whole document, complete with XML declaration, not just an element.
Change your LINQ query to:
IEnumerable<XElement> title =
from el in element.Descendants("Title")
select el;
Elements returns only the immediate children, Descendants returns all descendant nodes instead.
Descendants will select all the "Title" elements irrespective of the level. Please use xpath to correctly locate the element
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using System.IO;
public class Program
{
public static void Main()
{
string xmlFile = AppDomain.CurrentDomain.BaseDirectory + #"Capabilities\" + name + ".xml";
XElement xml=XElement.Load(xmlFile);
IEnumerable<XElement> titleElements = xml.XPathSelectElements("//Services/TileMapService/Title");
}
}

Modify xml, child nodes with same name

I am trying to modify a xmlString so i can create a dataset on the fly.
The xml looks like this:
<?xml version="1.0"?>
<ds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ttActivity>
<a-actno>1030371</a-actno>
<a-status>Start</a-status>
<a-solution/>
<a-descript>hei</a-descript>
<a-descript>hopp</a-descript>
<a-acttypegr>0</a-acttypegr>
<a-calltype/>
</ttActivity>
</ds>
The problem when creating a dataset using dataset.ReadXML(xmlReader) is the 2 nodes with same name "a-descript". Is there a quick way to fix this xml so that the nodes get unique names. ie: a-descript1 and a-descript2 ??
using LINQ to XML
XDocument doc = XDocument.Parse(xmlString);
doc.Descendants("a-descript").Last().Name = "a-descript2";
xmlString = doc.ToString();

xml Nodes by Element

Below is an example of the xml file that I need to pull data via C#. This is my first experience with reading xml files and a beginner with xml. Anyone have an example of how I would find/load the fieldorder values for Export_B?
<?xml version="1.0" encoding="utf-8"?>
<Config>
<OutFolderCSV>c:\Output\2012\upload_Files</OutFolderCSV>
<OutFolderImage>c:\Output\2012\NM_Scorecard_Images</OutFolderImage>
<PathOutLogFile>c:\Output\2012\Log\Matches.log</PathOutLogFile>
<FieldSeparator>,</FieldSeparator>
<ExportFile>
<Name>Export_A</Name>
<FieldOrder>matchID</FieldOrder>
<FieldOrder>contactID</FieldOrder>
<FieldOrder>stageID13</FieldOrder>
<FieldOrder>stringScore1a</FieldOrder>
<FieldOrder>xScore1a</FieldOrder>
<FieldOrder>stageID14</FieldOrder>
<FieldOrder>stringScore1b</FieldOrder>
<FieldOrder>xScore1b</FieldOrder>
<FieldOrder>stageID15</FieldOrder>
<FieldOrder>stringScore1c</FieldOrder>
<FieldOrder>xScore1c</FieldOrder>
</ExportFile>
<ExportFile>
<Name>Export_B</Name>
<FieldOrder>matchID</FieldOrder>
<FieldOrder>contactID</FieldOrder>
<FieldOrder>stageID16</FieldOrder>
<FieldOrder>stringScore1a</FieldOrder>
<FieldOrder>xScore1a</FieldOrder>
<FieldOrder>stageID17</FieldOrder>
<FieldOrder>stringScore1b</FieldOrder>
<FieldOrder>xScore1b</FieldOrder>
<FieldOrder>stageID18</FieldOrder>
<FieldOrder>stringScore1c</FieldOrder>
<FieldOrder>xScore</FieldOrder>
</ExportFile>
</Config>
Using LINQ to XML:
var doc = XDocument.Load(#"c:\path\to\file.xml");
var fieldOrders =
from exportFile in doc.Descendants("ExportFile")
where (string)exportFile.Element("Name") == "Export_B"
from fieldOrder in exportFile.Elements("FieldOrder")
select (string)fieldOrder;
I have written an article
http://www.codeproject.com/Articles/33769/Basics-of-LINQ-Lamda-Expressions
on XML using XDocument object.
You can parse the XML easily using
XDocument.Load(filepath)
Please read the section XLinq to parse the objects.
edit :
You can change value of Export_B using the code :
var document = XDocument.Load(filepath)
var exportFiles = document.Descandants("ExportFile");
List<XElement> list = new List<XElement>();
foreach(var element in exportFiles)
{
list.Add(element);
// Now you can do element.Element("Name") to get the name. Put a breakpoint on this, you can get the reference of all underlying objects.
}

Can I avoid having to use fully-qualified element names in LINQ to XML?

Say I call XElement.Parse() with the following XML string:
var xml = XElement.Parse(#"
<?xml version="1.0" encoding="UTF-8"?>
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Owner>
<ID>7c75442509c41100b6a413b88b523bd6f46554cdbee5b6cbe27bc08cb3f6a865</ID>
<DisplayName>me</DisplayName>
</Owner>
<AccessControlList>
<Grant>
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
...
");
When it comes time to query the element, I'm forced to use fully-qualified element names because that XML document contains an xmlns attribute in its root. This requires cumbersome creations of XName instances:
var AWS_XMLNS = "http://s3.amazonaws.com/doc/2006-03-01/";
var ownerElement = xml.Element(XName.Get("AccessControlPolicy", AWS_XMLNS)).Element(XName.Get("Owner", AWS_XMLNS));
When what I really want is simply,
var ownerElement = xml.Element("AccessControlPolicy").Element("Owner");
Is there a way to make LINQ to XML assume a specific namespace so I don't have to keep specifying it?
You could simplify by using
XNamespace ns = "http://s3.amazonaws.com/doc/2006-03-01/";
var ownerElement = xml.Element(ns + "AccessControlPolicy").Element(ns + "Owner");
I don't think you can (see Jon Skeet's comment), but there are a few tricks you can do.
1) create an extension method that appends the XNamespace to your string
2) Use VB?!?

Categories