LINQ to XML via C# - c#

I'm new to LINQ. I understand it's purpose. But I can't quite figure it out. I have an XML set that looks like the following:
<Results>
<Result>
<ID>1</ID>
<Name>John Smith</Name>
<EmailAddress>john#example.com</EmailAddress>
</Result>
<Result>
<ID>2</ID>
<Name>Bill Young</Name>
<EmailAddress>bill#example.com</EmailAddress>
</Result>
</Results>
I have loaded this XML into an XDocument as such:
string xmlText = GetXML();
XDocument xml = XDocument.Parse(xmlText);
Now, I'm trying to get the results into POCO format. In an effort to do this, I'm currently using:
var objects = from results in xml.Descendants("Results")
select new Results
// I'm stuck
How do I get a collection of Result elements via LINQ? I'm particularly confused about navigating the XML structure at this point in my code.
Thank you!

This will return a IEnumerable of anonymous class:
var q = from result in xml.Descendants
select new
{
ID = result.Descendants("ID"),
Name= result.Descendants("Name"),
EmailAddress= result.Descendants("EmailAddress")
};
or if you have defined class `Result, e.g.:
class Result
{
public ID { get; set; }
public Name { get; set; }
public EmailAddress { get; set; }
}
then:
var q = from result in xml.Descendants
select new Result
{
ID = result.Descendants("ID"),
Name = result.Descendants("Name"),
EmailAddress = result.Descendants("EmailAddress")
};
(returns IEnumerable<Result>)

If your Results child elements are only Result elements, then you can get them like this:
var objects = from result in xml.Descendants
select result;
But in this lucky case you can just use xml.Descendants.
If it's not only Result elements, then this will do fine:
var object = from result in xml.Descendants
where result.Name == "Result"
select result;

Related

XML to List in C#

This is the xml:
<Packages>
<Package>
<Id>1</Id>
<Prerequisites>
<Prerequisite>7</Prerequisite>
<Prerequisite>8</Prerequisite>
</Prerequisites>
</Package>
<Package>
<Id>2</Id>
.....
</Package>
....
</Packages>
And the list:
class lista
{
public int id {get; set;}
public List<int> pre{get;set;}
}
How can I add This xml pattern to a list of lista class and this is what i have got so far bot it only put one in the second list.
XDocument xdoc = XDocument.Load("Employee.xml");
var ListPckage =
(from item in xdoc.Descendants("Package")
orderby item.Element("Id").Value
select new
{
Id = item.Element("Id").Value,
Prerequisite = item.Element("Prerequisites").Element("Prerequisite").Value,
}).ToList();
foreach works for shoing them
foreach (var item in ListPckage)
{
Console.WriteLine(item.Id);
foreach (var item1 in ListPckage)
{
Console.WriteLine(item1.Prerequisites);
}
}
As John Sket mentioned in the comment to the question, one way to achieve that is to use Linq To Xml.
//string xcontent = #"xml content here";
//XDocument xdoc = XDocument.Parse(xcontent);
XDocument xdoc = XDocument.Load("FullPathToXml");
List<lista> resultlist = xdoc.Descendants("Package")
.Select(x=> new lista
{
id = Convert.ToInt32(x.Element("Id").Value),
pre = x.Descendants("Prerequisite").Select(y=>Convert.ToInt32(y.Value)).ToList()
})
.ToList();
But, i'd suggest to use XmlSerialization/XmlDeserialization.
First parse the XML to XDocument using XDocument.Parse to get XML in XDocument variable i.e.
var requiredXml = XDocument.Parse("Xml String here")
Then you can use LINQ to Xml (something like) as below:
//may be syntactic error but you can get an idea
var requiredList =
from element in requiredXml.Descandants("Package")
Select new lista { id = element.Element("ID").Value,
pre = element.Elements("Prerequisite").Select(x=> Convert.ToInt32(x.Value)).ToList() }

How to not deserialize contents of node

Say I have the following xml:
<Samples>
<Sample>
<SomeStuff>
<SomMoreStuff>.. </SomeMoreStuff>
</SomeStuff>
</Sample>
<Sample>
<SomeStuff>
<SomMoreStuff>.. </SomeMoreStuff>
</SomeStuff>
</Sample>
</Samples>
How can I deserilaize this but have all text inside of < Sample > remain as a string? I dont want to parse the contents of Sample
[XmlRoot(ElementName="Samples")]
public class Samples {
[XmlElement("Sample")]
public string[] Items{ get; set; }
}
I want to end of with a list like
[
"<Sample><SomeStuff><SomMoreStuff>.. </SomeMoreStuff></SomeStuff></Sample>"
"<Sample><SomeStuff><SomMoreStuff>.. </SomeMoreStuff></SomeStuff></Sample>"
]
You might want to load your Schema into the XmlDocument class and extract the inner or outer XML from it as a string.
One example could be:
var xdoc = new XmlDocument();
xdoc.LoadXml(MySchema);
var sampleNode = xdoc.SelectNodes("//sample");
var sampleText = sampleNode.ToString();
// or
var sampleText2 = sampleNode.Item(0).OuterXml;
Use debugging to check the actual value of the node, to get the right string as output.
List example:
var xdoc = new XmlDocument();
xdoc.LoadXml(MySchema);
var sampleNode = xdoc.SelectNodes("//sample");
var sampleList = new List<string>();
foreach (XmlNode item in sampleNode)
{
sampleList.Add(item.OuterXml); // or InnerXml - whatever value it is you need.
}

Querying XML elements with identical name with Linq

I have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<ProductTypes>
<ProductType Name="MyProduct">
<Amount>100</Amount>
<Pattern Length="1" AllowedCharacters="ABCD"/>
<Pattern Length="7" AllowedCharacters="EFGH"/>
</ProductType>
</ProductTypes>
Using Linq to XML I can successfully extract information from any number of the element ProductType. However I also need the information from all of the elements Pattern.
XElement xml = XElement.Load("pattern_config.xml");
var productTypes = from productType in xml.Elements("ProductType")
select new {
Name = productType.Attribute("Name").Value,
Amount = Convert.ToInt32(productType.Element("Amount").Value)
// How to get all Pattern elements from that ProductType?
};
How can I do this? Or would you recommend another way accessing this XML?
You can nest queries.
var productTypes = from productType in xml.Elements("ProductType")
select new {
Name = productType.Attribute("Name").Value,
Amount = Convert.ToInt32(productType.Element("Amount").Value),
// How to get all Pattern elements from that ProductType?
Patterns = from patt in productType.Elements("Pattern")
select new { Length = int.Parse(patt.Attribute("Length").Value),
.... }
};

I'm confused by Linq XML query

Here is my XML sample. I want to select SystemSetting's value if ID = 123. But I can't figure out how. How can I select SystemSetting value if id's value equal to 123 ?
<?xml version="1.0" encoding="utf-8" ?>
<Private>
<System>
<ID>123</ID>
<NAME>Test</NAME>
<SystemSetting>128</SystemSetting>
<SystemSettingCS>127</SystemSettingCS>
</System>
<System>
<ID>124</ID>
<NAME>Test2</NAME>
<SystemSetting>128</SystemSetting>
<SystemSettingCS>127</SystemSettingCS>
</System>
<System>
<ID>0</ID>
<NAME>Test</NAME>
<SystemSetting>5</SystemSetting>
<SystemSettingCS>250</SystemSettingCS>
</System>
</Private>
Here's what I tried:
var doc = XDocument.Load(Application.StartupPath+ #"\Settings.xml");
var q = from Ana in doc.Descendants("Private")
from sistem in Ana.Elements("System")
where (int)sistem.Element("ID") == 123
from assetText in Sistem.Elements("System")
select assetText.Element("SystemSetting");
MessageBox.Show(q.ToString());
thnx for help.
I think you're making this more complicated than you need to. I think you just need:
var query = doc.Descendants("Private") // Or just doc.Root
.Elements("System")
.Where(x => (int) x.Element("ID") == 123)
.Select(x => x.Element("SystemSetting"))
.FirstOrDefault();
That will select the first matching element, admittedly. The type of query is then XElement; if you take off the FirstOrDefault() part, it will return an IEnumerable<XElement>, for all matching elements.
If you want just the value instead of the element, you can change the Select to:
.Select(x => (string) x.Element("SystemSetting"))
or
.Select(x => x.Element("SystemSetting").Value)
(The first will return null if there's no SystemSetting element; the second will throw an exception.)
Xpath (System.Xml.XPath) can really help here
var system = doc.XPathSelectElement("//System[ID[text()='123']]");
var val = system.Element("SystemSetting").Value;
or with a single line
var s = (string)doc.XPathSelectElement("//System[ID[text()='123']]/SystemSetting");
Your almost there
var xmlFile = XElement.Load(#"c:\\test.xml");
var query =
from e in xmlFile.Elements()
where e.Element("ID").Value == "123"
select e.Element("SystemSetting").Value;
var q = from s in doc.Descendants("System")
where (int)s.Element("ID") == 123
select (int)s.Element("SystemSetting");
And show result (q will have IEnumerable<int> type):
if (q.Any())
MessageBox.Show("SystemSettings = " + q.First());
else
MessageBox.Show("System not found");
Was a Linq question, but there is an alternate XPath approach, but the class defined below could work in either scenario.
Define a class to read from the parent System element:
public class XSystem
{
public XSystem(XElement xSystem) { self = xSystem; }
XElement self;
public int Id { get { return (int)self.Element("ID"); } }
public string Name { get { return self.Element("NAME").Value; } }
public int SystemSetting { get { return (int)self.Element("SystemSetting"); } }
public int SystemSettingCS { get { return (int)self.Element("SystemSettingCS"); } }
}
Then find your System element that has a child ID element of 123.
int id = 123;
string xpath = string.Format("//System[ID={0}", id);
XElement x = doc.XPathSelectElement(xpath);
Then plug it into the class:
XSystem system = new XSystem(x);
Then read the value you want:
int systemSetting = system.SystemSetting;
XPath is defined with using System.Xml.XPath;

What is a good way to read this XML?

What is a good way to read this XML? Or maybe I can structure the XML differently.
What I want is the process to be the main thing and then you could have any number of related process to follow.
<Job>
<Process>*something.exe</Process>
<RelatedToProcess>*somethingelse.exe</RelatedToProcess>
<RelatedToProcess>*OneMorething.exe</RelatedToProcess>
</Job>
I am currently using a XmlNodeList and reading the innertext and then splitting the string on * but I know there has to be a better way.
I suggest you to use Linq To Xml. You can load your xml in XDocument and then access each separate XElement by name or path.
Try this console app:
class Program
{
public class Job
{
public string Process { get; set; }
public IList<string> RelatedProcesses { get; set; }
}
static void Main(string[] args)
{
var xml = "<Job>" +
"<Process>*something.exe</Process>" +
"<RelatedToProcess>*somethingelse.exe</RelatedToProcess>" +
"<RelatedToProcess>*OneMorething.exe</RelatedToProcess>" +
"</Job>";
var jobXml = XDocument.Parse(xml);
var jobs = from j in jobXml.Descendants("Job")
select new Job
{
Process = j.Element("Process").Value,
RelatedProcesses = (from r in j.Descendants("RelatedToProcess")
select r.Value).ToList()
};
foreach (var t in jobs)
{
Console.WriteLine(t.Process);
foreach (var relatedProcess in t.RelatedProcesses)
{
Console.WriteLine(relatedProcess);
}
}
Console.Read();
}
}
I like to use XmlSerializer for simple tasks like this, as it results in significantly less code to process the XML. All you need is a simple class that maps to your XML, e.g.
public class Job
{
public string Process { get; set; }
[XmlElement("RelatedToProcess")]
public List<string> RelatedProcesses { get; set; }
}
You can then read the XML like this:
XmlSerializer serializer = new XmlSerializer(typeof(Job));
using (var reader = XmlReader.Create(#"d:\temp\test.xml"))
{
Job j = (Job)serializer.Deserialize(reader);
Console.WriteLine(j.Process);
Console.WriteLine(j.RelatedProcesses.Count);
j.RelatedProcesses.ForEach(p => Console.WriteLine(p));
}
you can use XDocument and XElement
If you want the Process to be "main thing" I would recommend restructuring XML as
<Job>
<Process Name="*.something.exe">
<RelatedToProcess>*somethingelse.exe</RelatedToProcess>
<RelatedToProcess>*OneMorething.exe</RelatedToProcess>
</Process>
</Job>
This way you can easily get all child elements of "Process" element...And logically they relate (at least to me :)) Whether you use attribute or element is a matter of style...
XDocument for sure!
XDocument xDoc = XDocument.Load("Process.xml");
var process = from p in xDoc.Descendants("Process")
select p.Value;
var relatedProcess = from p in xDoc.Descendants("RelatedToProcess")
select p.Value;
Console.WriteLine("Process: {0}", process.ElementAtOrDefault(0).ToString());
foreach (var p in relatedProcess)
Console.WriteLine("RelatedToProcess: {0}", p);
The code will print:
Process: *something.exe
RelatedToProcess: *somethingelse.exe
RelatedToProcess: *OneMorething.exe
I would actually store it slightly different. Concept is that you create a "Process" complex type and then you reuse it within RelatedProcess and you can make name an attribute if desired.
<Job>
<Process>something.exe</Process>
<RelatedProcess>
<Process>
<Name>somethingelse.exe</Name>
</Process>
<Process>
<Name>OneMorething.exe
</Name>
</Process>
</RelatedProcess>
</Job>
That would allow for better growth. For instance if you decided to have recursive processeses i.e.:
<Job>
<Process>
<Name>something.exe</Name>
<RelatedProcess>
<Process>
<Name>somethingelse.exe</Name>
<RelatedProcess>
<Process>
<Name>recursive.exe</Name>
</Process>
</RelatedProcess>
</Process>
<Process>
<Name>OneMorething.exe</Name>
</Process>
</RelatedProcess>
</Process>
</Job>
Here is an XDocument example.. I did not show the recursive creation of processes because i wasn't sure if you wanted to use it.
string xml = "<Job>...xml here ";
XDocument doc = XDocument.Parse(xml);
var Processess = from process in doc.Elements("Job").Elements("Process")
select new
{
ProcessName = process.Element("Name"),
RelatedProcesses = (from rprocess in process.Elements("RelatedProcess").Elements("Process")
select new
{
ProcessName = rprocess.Element("Name")
}
).ToList()
};
Let me know if you have questions.

Categories

Resources