I have this XML file settings , How can I (USING LINQ) return the list of (StartItem) to ArrayList
and return( PriceItem) to another ArrayList
<Settings>
<ConSetting SettingID="1">
<Company CompanyID="1" CompanyName="CA" Code="*100#" Pin="11111" MobileName="M1">
<StartItems StartID="1"> 094</StartItems>
<StartItems StartID="2"> 095</StartItems>
<StartItems StartID="4"> 097</StartItems>
<StartItems StartID="5"> 098</StartItems>
</Company>
<Company CompanyID="2" CompanyName="CB" Code="*200#" Pin="22222" MobileName="M2">
<StartItems StartID="1"> 099</StartItems>
<StartItems StartID="2"> 093</StartItems>
<StartItems StartID="3"> 091</StartItems>
<StartItems StartID="4"> 092</StartItems>
</Company>
</ConSetting>
<Price SettingID ="2" CompanyName="CA" >
<Company CompanyID="1">
<PriceItem P="40"> 50</PriceItem>
<PriceItem P="90"> 100</PriceItem>
<PriceItem P="200"> 225</PriceItem>
</Company>
<Company CompanyID="2" CompanyName="CB" >
<PriceItem P="40"> 60</PriceItem>
<PriceItem P="90"> 110</PriceItem>
<PriceItem P="200"> 235</PriceItem>
</Company>
</Price>
</Settings>
XDocument is easy to use in your case:
var doc = XDocument.Load("settings.xml");
var result = from items in doc.Descendants("StartItems")
where items.Parent.Attribute("CompanyID").Value == "1"
select new StartItem()
{
StartID = items.Attribute("StartID").Value,
Value = items.Value
};
var Company1List = new ArrayList();
foreach(var item in result)
{
Company1List.Add(item);
}
public class StartItem
{
public string StartID { get; set; }
public string Value { get; set; }
}
You might want to look at the XmlReader Class in .NET
It should easily be able to parse this XML and then you can select certain items from the nodes into any list you want.
http://msdn.microsoft.com/en-us/library/system.xml.xmlreader.aspx
Look at this question and answer
Reading Xml with XmlReader in C#
Related
I am trying to add my code for already existing code. I have below two xmls:
Out.xml
<School>
<Classes>
<Class>
<Students>
<Student SequenceNumber="1">
<ID>123</ID>
<Name>AAA</Name>
</Student>
</Students>
</Class>
</Classes>
</School>
In.xml
<School>
<Classes>
<Class>
<Students>
<Student SequenceNumber="1">
<ID>456</ID>
<Name>BBB</Name>
</Student>
<Student SequenceNumber="2">
<ID>123</ID>
<Name>AAA</Name>
</Student>
<Student SequenceNumber="3">
<ID>789</ID>
<Name>CCC</Name>
</Student>
</Students>
</Class>
</Classes>
</School>
Now I need to check Out.xml and In.xml and my final Out.xml must be like below. The rule here is check StudentID in Out and In xmls. If Out xml doesnot have it and In xml has it add it to Out.xml at end of already existing elements.
Out.xml
<School>
<Classes>
<Class>
<Students>
<Student SequenceNumber="1">
<ID>123</ID>
<Name>AAA</Name>
</Student>
<Student SequenceNumber="2">
<ID>456</ID>
<Name>BBB</Name>
</Student>
<Student SequenceNumber="3">
<ID>789</ID>
<Name>CCC</Name>
</Student>
</Students>
</Class>
</Classes>
</School>
Already existing code is as below
string inFileName = #"C:\In.xml";
string inXml = System.IO.File.ReadAllText(inFileName);
var xmlReaderSource = XmlReader.Create(new StringReader(inXml));
var mgr = new XmlNamespaceManager(xmlReaderSource.NameTable);
mgr.AddNamespace("m", "http://www.mismo.org/residential/2009/schemas");
XDocument sourceXmlDoc = XDocument.Load(xmlReaderSource);
string outFileName = #"C:\Out.xml";
string outXml = System.IO.File.ReadAllText(outFileName);
XmlDocument targetXmlDoc = new XmlDocument();
targetXmlDoc.LoadXml(outXml);
I cannot change above code now I need add my logic.
I added like below
string xpath = #"/m:School/m:Classes/m:Class/m:Students";
XmlNodeList outStudentNodes = targetXmlDoc.SelectNodes(xpath + "/m:Student", namespaceManager);
if(outStudentNodes== null || outStudentNodes.Count <= 0)
{
return;
}
XElement root = sourceXmlDoc.Root;
IEnumerable<XElement> inStudentsColl = from item in root.Elements("Classes").Descendants("Class")
.Descendants("Students").Descendants("Student")
select item;
Now I have XmlNodeList and IEnumerble, trying to see whether I can use LINQ statement and make code simple for my comparison.
Node: I am not asking how to add nodes/elements using C#. I am looking for how to compare two xmls and then add nodes/elements into the one which is missing those nodes/elements. My issue here is one xml is read like XDocument and other using XmlDocument.
UPDATE
Thank you very much #TheAnatheme. I really appreciate it.
I followed what TheAnatheme suggested me and it worked. I marked TheAnatheme's answer as real solution. Please see below what I did in foreach block so that if anyone wants to use they can refer to this post.
string xpath = #"/m:School/m:Classes/m:Class/m:Students
XmlNode studentsNode = targetXmlDoc.SelectSingleNode(xpath, namespaceManager);
foreach (var element in elementsToAdd)
{
//Add Microsoft.CSharp.dll (if needed ) to your project for below statement to work
dynamic studentElement = element as dynamic;
if (studentElement != null)
{
XmlElement studentXmlElement = targetXmlDoc.CreateElement("Student");
XmlElement studentIDXmlElement = targetXmlDoc.CreateElement("ID");
studentIDXmlElement.InnerText = studentElement.ID;
XmlElement studentNameXmlElement = targetXmlDoc.CreateElement("Name");
studentNameXmlElement .InnerText = studentElement.Name;
studentXmlElement.AppendChild(studentIDXmlElement);
studentXmlElement.AppendChild(studentNameXmlElement);
studentsNode.AppendChild(childElement);
}
}
This projects both sets into an anonymous object List, makes comparisons, and gives you a set of anonymous objects that don't yet exist by which you can add to the out XML.
public static List<object> GetInStudents(XDocument sourceXmlDoc)
{
IEnumerable<XElement> inStudentsElements =
sourceXmlDoc.Root.Elements("Classes").Descendants("Class")
.Descendants("Students").Descendants("Student");
return inStudentsElements.Select(i =>
new { Id = i.Elements().First().Value,
Name = i.Elements().Last().Value }).Cast<object>().ToList();
}
public static List<object> GetOutStudents(XmlDocument targetXmlDoc)
{
XmlNodeList outStudentsElements = targetXmlDoc.GetElementsByTagName("Students")[0].ChildNodes;
var outStudentsList = new List<object>();
for (int i = 0; i < outStudentsElements.Count; i++)
{
outStudentsList.Add(new { Id = outStudentsElements[i].ChildNodes[0].InnerText,
Name = outStudentsElements[i].ChildNodes[1].InnerText });
}
return outStudentsList;
}
And you compare them as such:
var inStudents = GetInStudents(sourceXmlDoc);
var outStudents = GetOutStudents(targetXmlDoc);
if (inStudents.SequenceEqual(outStudents))
{
return;
}
else
{
var elementsToAdd = inStudents.Except(outStudents);
foreach (var element in elementsToAdd)
{
// create xmlNode with element properties, add element to xml
}
}
I'm trying to use the contents of an XML file as the data source to a List of objects. The object looks like this:
public class QuestionData
{
public string QuestionName{get;set;}
public List<string> Answers{get;set;}
}
And here is my XML:
<?xml version="1.0" encoding="utf-8" ?>
<QuestionData>
<Question>
<QuestionName>Question 1</QuestionName>
<Answers>
<string>Answer 1</string>
<string>Answer 2</string>
<string>Answer 3</string>
<string>Answer 4</string>
</Answers>
</Question>
<Question>
<QuestionName>Question 2</QuestionName>
<Answers>
<string>Answer 1</string>
<string>Answer 2</string>
<string>Answer 3</string>
</Answers>
</Question>
</QuestionData>
The code I'm using to try and do this is:
var xml = XDocument.Load ("C:\temp\xmlfile.xml");
List<QuestionData> questionData = xml.Root.Elements("Question").Select
(q => new QuestionData {
QuestionName = q.Element ("QuestionName").Value,
Answers = new List<string> {
q.Element ("Answers").Value }
}).ToList ();
The code compiles, but I'm not getting any data from the XML. I looped through questionData to try and display the information to the console but it was empty.
List<QuestionData> questionData =
xml.Root
.Elements("Question")
.Select(q => new QuestionData
{
QuestionName = (string)q.Element("QuestionName"),
Answers = q.Element("Answers")
.Elements("string")
.Select(s => (string)s)
.ToList()
}).ToList();
I used (string)XElement cast instead of XElement.Value property because it doesn't throw NullReferenceException when element is null.
Xml is as follows.
<System>
<ID></ID>
<Name></Name>
<Monitor>
<ID></ID>
<Type></Type>
<Alert>
<ID></ID>
<Status></Status>
</Alert>
<Alert>
<ID></ID>
<Status></Status>
</Alert>
</Monitor>
</System>
<System>
<ID></ID>
<Name></Name>
<Monitor>
<ID></ID>
<Type></Type>
<Alert>
<ID></ID>
<Status></Status>
</Alert>
</Monitor>
</System>
I want to traverse it like this
XElement xmlDoc = XElement.Load(#"xml");
var q = from el in xmlDoc.Elements("System") select el;
foreach(el in q) {
Console.WriteLine(el.ID);
Console.WriteLine(el.Name);
if (el.Monitor) {
foreach (mon in el.Monitor) {
Console.WriteLine(el.ID);
Console.WriteLine(el.Type);
if (mon.Alert) {
foreach (alert in mon.Alert) {
Console.WriteLine(alert.ID);
Console.WriteLine(alert.Status);
}
}
}
}
}
Currently I loop through each several times and use if to check field and then assign value to a variable. Then I have to loop through it again. Is there an easier way, and should I use plain LINQ or LINQ-TO-XML?
Ok try this code (see below code for explaination)
class Program {
static void Main(string[] args) {
var xml = #"
<Root>
<System>
<ID>1</ID>
<Name>one</Name>
<Monitor>
<ID>3</ID>
<Type>t3</Type>
<Alert>
<ID>5</ID>
<Status>a5</Status>
</Alert>
<Alert>
<ID>6</ID>
<Status>a6</Status>
</Alert>
</Monitor>
</System>
<System>
<ID>2</ID>
<Name>two</Name>
<Monitor>
<ID>4</ID>
<Type>t4</Type>
<Alert>
<ID>7</ID>
<Status>a7</Status>
</Alert>
</Monitor>
</System>
</Root>
";
XElement xmlDoc = XElement.Parse(xml);
// set q to an enumeration of XElements
// where the elements xname is "System"
// the query actually executes the first time q is used
var q = xmlDoc.Elements("System");
foreach (var ele in q) {
// Get the value of the Element with the xname of "ID"
Console.WriteLine(ele.Element("ID").Value);
Console.WriteLine(ele.Element("Name").Value);
// if ele.Elements("Monitor") returns nothing
// then the foreach will be skipped (null-execution)
foreach (var mon in ele.Elements("Monitor")) {
Console.WriteLine(mon.Element("ID").Value);
Console.WriteLine(mon.Element("Type").Value);
foreach (var alert in mon.Elements("Alert")) {
Console.WriteLine(alert.Element("ID").Value);
Console.WriteLine(alert.Element("Status").Value);
}
}
}
}
}
This code will move through the XML document exactly once. In C# LINQ contains both language elements (like 'select' and 'from') and library element (.NET framework methods like XDocument.Elements); mixing the two is ok, but should only be done with understanding of what is occurring behind the statements. In this case you are asking for the XDocument to return all the child elements with an XName of "System". In the above code 'q' does not receive all of the elements, it receives an enumeration which can be iterated. The assignment of q is a very low cost operation because the XDocument contents are not transverse until the first foreach and then only one element at a time is examined. Do a search for "C# yield return" to see how this is implemented.
If you were only interested in the "Alert" elements you could do something like this:
var alerts = xmlDoc.Descendants("Alert")
This would return an enumeration of all elements with an XName of "Alert" (regardless of where they are in the hierarchy of the XML document). If you wanted to ensure the hierarchy you can use 'where', for example:
var alerts = xmlDoc.Descendants("Alert")
.Where(ele => (ele.Parent != null) && (ele.Parent.Name == "Monitor"))
.Where(ele => (ele.Parent.Parent != null) && (ele.Parent.Parent.Name == "System"));
foreach (var alert in alerts) {
Console.WriteLine(alert.Element("ID").Value);
Console.WriteLine(alert.Element("Status").Value);
}
If you need to iterate over the same nodes multiple times you should consider converting the enumeration to a list or array, this saves time but increases memory usage. IEnumerable<> has extension methods ".ToArray()" and ".ToList()" for this purpose.
C# is an OOP language, I think you should harness that for this:
Example:
public class MySystem
{
public int Id { get; private set; }
public string Name { get; private set; }
public MyMonitor[] Monitors { get; private set; }
public MySystem(XElement x)
{
Id = (int)x.Element("ID");
Name = x.Element("Name").Value;
// a little confusing from your code if there can be more than one Monitor
Monitors = x.Elements("Monitor").Select(m => new MyMonitor(m)).ToArray();
}
}
Do something similar for your Monitor class and your Alert class. I named it MySystem since a class named System is a mess.
You create your array of systems like I created the Monitor's array above with:
XElement xmlDoc = XElement.Load(#"xml");
MySystem[] systems = xmlDoc.Elements("System")
.Select(s => new MySystem(s))
.ToArray();
Now you have all your values in easy to use classes.
If you want to traverse it like that you can:
var xml = #"
<Root>
<System>
<ID>1</ID>
<Name>One</Name>
<Monitor>
<ID>2</ID>
<Type>Two</Type>
<Alert>
<ID>3</ID>
<Status>Three</Status>
</Alert>
<Alert>
<ID>4</ID>
<Status>Four</Status>
</Alert>
</Monitor>
</System>
<System>
<ID>5</ID>
<Name>Five</Name>
<Monitor>
<ID>6</ID>
<Type>Six</Type>
<Alert>
<ID>7</ID>
<Status>Seven</Status>
</Alert>
</Monitor>
</System>
</Root>
";
XElement xmlDoc = XElement.Parse(xml);
var q = xmlDoc.Elements("System");
foreach(var el in q) {
Console.WriteLine(el.Element("ID").Value);
Console.WriteLine(el.Element("Name").Value);
foreach(var mon in el.Elements("Monitor")) {
Console.WriteLine(mon.Element("ID").Value);
Console.WriteLine(mon.Element("Type").Value);
foreach(var alert in mon.Elements("Alert")) {
Console.WriteLine(alert.Element("ID").Value);
Console.WriteLine(alert.Element("Status").Value);
}
}
}
I have an XML in a string variable i want to check if this xml content contains
<xml>
<message display='yes'>
....
or
<xml>
<xdp:xdp>
...
is it possible?
Here is how you can check for it:
string example = #"<xml>
<message display='yes'></message>
</xml>";
XDocument doc = XDocument.Parse(example);
if (doc.Element("xml").Element("message") != null)
{
// node "message" exists within node "xml" which is located at the root of the document
}
You can use XDocument class to check for existence of any node at any location in the hierarchy of an XML document. You can load contents from string or file easily.
An example below
<Order>
<AmazonOrderID>000-1111111-2222222</AmazonOrderID>
<MerchantOrderID>111-3333333-4444444</MerchantOrderID>
<PurchaseDate>2012-03-02T13:28:53+00:00</PurchaseDate>
<LastUpdatedDate>2012-03-02T13:29:05+00:00</LastUpdatedDate>
<OrderStatus>Pending</OrderStatus>
<SalesChannel>Amazon.com</SalesChannel>
<URL>http://www.amazon.com</URL>
<FulfillmentData>
<FulfillmentChannel>Amazon</FulfillmentChannel>
<ShipServiceLevel>Standard</ShipServiceLevel>
<Address>
<City>Beverly Hills</City>
<State>CA</State>
<PostalCode>90210-1234</PostalCode>
<Country>US</Country>
</Address>
</FulfillmentData>
<OrderItem>
<ASIN>AmazonASIN </ASIN>
<SKU> Internal-SKU</SKU>
<ItemStatus>Pending</ItemStatus>
<ProductName> This is the name of the product </ProductName>
<Quantity>1</Quantity>
<ItemPrice>
<Component>
<Type>Principal</Type>
<Amount currency="USD">19.99</Amount>
</Component>
</ItemPrice>
</OrderItem>
</Order>
List<string> getNodes(string path, string nodeName) {
List<string> nodes = new List<string>();
XDocument xmlDoc = XDocument.Load(path); //Create the XML document type
foreach (var el in xmlDoc.Descendants(nodeName)) {
//for debugging
//nodes.Add(el.Name + " " + el.Value);
//for production
nodes.Add(el.Value);
}
return nodes;
} //end getNodes
List<string> skuNodes = xml.getNodes(#"AmazonSalesOrders.xml", "SKU");
I've got some XML I'm trying to import with c#, which looks something like this:
<root>
<run>
<name = "bob"/>
<date = "1958"/>
</run>
<run>
<name = "alice"/>
<date = "1969"/>
</run>
</root>
I load my xml using
XElement xDoc=XElement.Load(filename);
What I want to do is have a class for "run", under which I can store names and dates:
public class RunDetails
{
public RunDetails(XElement xDoc, XNamespace xmlns)
{
var query = from c in xDoc.Descendants(xmlns + "run").Descendants(xmlns + "name") select c;
int i=0;
foreach (XElement a in query)
{
this.name= new NameStr(a, xmlns); // a class for names
Name.Add(this.name); //Name is a List<NameStr>
i++;
}
// Here, i=2, but what I want is a new instance of the RunDetails class for each <run>
}
}
How can I set up my code to create a new instance of the RunDetails class for every < run>, and to only select the < name> and < date> inside a given < run>?
You can just LINQ to XML to create an IEnumerable from your XML.
IEnumerable<RunDetail> runDetails = from run in xdocument.Descendants("run")
select new RunDetail
{
Name = run.Element("name").Value,
Date = int.Parse(run.Element("date").Value)
};
This, of course, suggests there's a class named RunDetail with public Name and Date (int for the year) properties. You can iterate over the enumerable as it is, or if you need more explicit access to the individual members, you can use .ToList() or .ToArray() to convert the query.
You need to have some parent element in your xml cause your one is not valid. If you have following xml
<root>
<run>
<name = "bob"/>
<date = "1958"/>
</run>
<run>
<name = "alice"/>
<date = "1969"/>
</run>
</root>
you can load it to XDocument and iterate through children of the root element. For each run child element you can create RunDetails.