What is a good way to read this XML? - c#

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.

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.
}

Get certain xml node and save the value

Considering the following XML:
<Stations>
<Station>
<Code>HT</Code>
<Type>123</Type>
<Names>
<Short>H'bosch</Short>
<Middle>Den Bosch</Middle>
<Long>'s-Hertogenbosch</Long>
</Names>
<Country>NL</Country>
</Station>
</Stations>
There are multiple nodes. I need the value of each node.
I've got the XML from a webpage (http://webservices.ns.nl/ns-api-stations-v2)
Login (--) Pass (--)
Currently i take the XML as a string and parse it to a XDocument.
var xml = XDocument.Parse(xmlString);
foreach (var e in xml.Elements("Long"))
{
var stationName = e.ToString();
}
You can retrieve "Station" nodes using XPath, then get each subsequent child node using more XPath. This example isn't using Linq, which it looks like you possibly are trying to do from your question, but here it is:
XmlDocument xml = new XmlDocument();
xml.Load(xmlStream);
XmlNodeList stations = xml.SelectNodes("//Station");
foreach (XmlNode station in stations)
{
var code = station.SelectSingleNode("Code").InnerXml;
var type = station.SelectSingleNode("Type").InnerXml;
var longName = station.SelectSingleNode("Names/Long").InnerXml;
var blah = "you should get the point by now";
}
NOTE: If your xmlStream variable is a String, rather than a Stream, use xml.LoadXml(xmlStream); for line 2, instead of xml.Load(xmlStream). If this is the case, I would also encourage you to name your variable to be more accurately descriptive of the object you're working with (aka. xmlString).
This will give you all the values of "Long" for every Station element.
var xml = XDocument.Parse(xmlStream);
var longStationNames = xml.Elements("Long").Select(e => e.Value);

LINQ to XML via 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;

C# newbie: reading repetitive XML to memory

I'm new to C#. I'm building an application that persists an XML file with a list of elements. The structure of my XML file is as follows:
<Elements>
<Element>
<Name>Value</Name>
<Type>Value</Type>
<Color>Value</Color>
</Element>
<Element>
<Name>Value</Name>
<Type>Value</Type>
<Color>Value</Color>
</Element>
<Element>
<Name>Value</Name>
<Type>Value</Type>
<Color>Value</Color>
</Element>
</Elements>
I have < 100 of those items, and it's a single list (so I'm considering a DB solution to be overkill, even SQLite). When my application loads, I want to read this list of elements to memory. At present, after browsing the web a bit, I'm using XmlTextReader.
However, and maybe I'm using it in the wrong way, I read the data tag-by-tag, and thus expect the tags to be in a certain order (otherwise the code will be messy). What I would like to do is read complete "Element" structures and extract tags from them by name. I'm sure it's possible, but how?
To clarify, the main difference is that the way I'm using XmlTextReader today, it's not tolerant to scenarios such as wrong order of tags (e.g. Type comes before Name in a certain Element).
What's the best practice for loading such structures to memory in C#?
It's really easy to do in LINQ to XML. Are you using .NET 3.5? Here's a sample:
using System;
using System.Xml.Linq;
using System.Linq;
class Test
{
[STAThread]
static void Main()
{
XDocument document = XDocument.Load("test.xml");
var items = document.Root
.Elements("Element")
.Select(element => new {
Name = (string)element.Element("Name"),
Type = (string)element.Element("Type"),
Color = (string)element.Element("Color")})
.ToList();
foreach (var x in items)
{
Console.WriteLine(x);
}
}
}
You probably want to create your own data structure to hold each element, but you just need to change the "Select" call to use that.
Any particular reason you're not using XmlDocument?
XmlDocument myDoc = new XmlDocument()
myDoc.Load(fileName);
foreach(XmlElement elem in myDoc.SelectNodes("Elements/Element"))
{
XmlNode nodeName = elem.SelectSingleNode("Name/text()");
XmlNode nodeType = elem.SelectSingleNode("Type/text()");
XmlNode nodeColor = elem.SelectSingleNode("Color/text()");
string name = nodeName!=null ? nodeName.Value : String.Empty;
string type = nodeType!=null ? nodeType.Value : String.Empty;
string color = nodeColor!=null ? nodeColor.Value : String.Empty;
// Here you use the values for something...
}
It sounds like XDocument, and XElement might be better suited for this task. They might not have the absolute speed of XmlTextReader, but for your cases they sound like they would be appropriate and it would make dealing with fixed structures a lot easier. Parsing out elements would work like so:
XDocument xml;
foreach (XElement el in xml.Element("Elements").Elements("Element")) {
var name = el.Element("Name").Value;
// etc.
}
You can even get a bit fancier with Linq:
XDocument xml;
var collection = from el in xml.Element("Elements").Elements("Element")
select new { Name = el.Element("Name").Value,
Color = el.Element("Color").Value,
Type = el.Element("Type").Value
};
foreach (var item in collection) {
// here you can use item.Color, item.Name, etc..
}
You could use XmlSerializer class (http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx)
public class Element
{
public string Name { get; set; }
public string Type { get; set; }
public string Color { get; set; }
}
class Program
{
static void Main(string[] args)
{
string xml =
#"<Elements>
<Element>
<Name>Value</Name>
<Type>Value</Type>
<Color>Value</Color>
</Element>(...)</Elements>";
XmlSerializer serializer = new XmlSerializer(typeof(Element[]), new XmlRootAttribute("Elements"));
Element[] result = (Element[])serializer.Deserialize(new StringReader(xml));}
You should check out Linq2Xml, http://www.hookedonlinq.com/LINQtoXML5MinuteOverview.ashx

Categories

Resources