Let me put the scenario:
If the 'Student' node has same child elements then merge the 'Student' node. In this case if the 'Name' node is found in other 'Student' nodes with same value then those 2 'Student' nodes need to be merged with unique elements. In this case the 'Name' node being identical comes 1 time and the 'Address' node coming 2 times.
Also the input xml can have different set of child nodes and can have different names every time.
Below is Input xml
<Root>
<Student>
<Name>Tim</Name>
<Address>
<City>
<Location1>MEL</Location1>
</City>
</Address>
</Student>
<Student>
<Name>Tim</Name>
<Address>
<City>
<Location1>DEL</Location1>
</City>
</Address>
</Student>
<Student>
<Name>1</Name>
<FatherName>Papa</FatherName>
<Address>
<Suburb>1</Suburb>
<City>
<Location1>HNL</Location1>
</City>
</Address>
</Student>
<Student>
<Name>1</Name>
<MotherName>Mom</MotherName>
<Address>
<City>
<Location1>HNL</Location1>
</City>
</Address>
</Student>
</Root>
Expected xml:
<Root>
<Student>
<Name>Tim</Name>
<Address>
<City>
<Location1>MEL</Location1>
</City>
</Address>
<Address>
<City>
<Location1>DEL</Location1>
</City>
</Address>
</Student>
<Student>
<Name>1</Name>
<FatherName>Papa</FatherName>
<Address>
<Suburb>1</Suburb>
<City>
<Location1>HNL</Location1>
</City>
</Address>
</Student>
<Student>
<Name>1</Name>
<MotherName>Mom</MotherName>
<Address>
<City>
<Location1>HNL</Location1>
</City>
</Address>
</Student>
</Root>
I tried to implement with the below code. I know its not very efficient.
var newdoc = XDocument.Parse(input);
// 'restriction' is the concerned node
foreach (var element in newdoc.Descendants("restriction"))
{
if (skiptimes > 0)
{
skiptimes--;
continue;
}
//Get distinct node names for 'element'
var distinctNodeName = element.Elements().Select(cc => cc.Name).Distinct();
//delete if found 'freetext' node as the this do not need to be comapared
distinctNodeName = distinctNodeName.Where(n => n.LocalName.ToString() != "FreeText");
//Get distinct elements
var distinctElementName = element.Elements().Select(xx => xx).Distinct();
foreach (var nextelement in element.ElementsAfterSelf())
{
if (!nextelement.IsEmpty)
{
//Get distinct node names for 'nextelement'
var distinctNodeName2 = nextelement.Elements().Select(xx => xx.Name).Distinct();
//delete if found 'freetext' node as the this do not need to be comapared
distinctNodeName2 = distinctNodeName2.Where(n => n.LocalName.ToString() != "FreeText");
//Get distinct elements
var distinctElements2 = nextelement.Elements().Select(xx => xx).Distinct();
//From 'element' excluding the 'StopoverSegs' node which by default always come as last node
var subelements = element.Elements().Take(distinctNodeName.Count() - 1);
//From 'nextelement' excluding the 'StopoverSegs' node which by default always come as last node
var sub2 = nextelement.Elements().Take(distinctNodeName2.Count() - 1);
// Compare node name counts which are selected as distinct
if (distinctNodeName.Count() == distinctNodeName2.Count())
{
ArrayList fir = new ArrayList();
int arrcount = 0; ArrayList sec = new ArrayList();
//Add 'element' to array list for comparison
foreach (var firstSet in subelements)
{
fir.Add(firstSet.ToString()); arrcount++;
}
//Add 'nextelement' to array list for comparison
foreach (var secondSet in sub2)
{
sec.Add(secondSet.ToString());
}
//comparison . I could not get through to compare via SequenceEqual or other custom
//extension
for (int i = 0; i < arrcount; i++)
{
if (fir[i].ToString().Trim() != sec[i].ToString().Trim())
{
GotEqual = false;
break;
}
else
{
GotEqual = true;
}
}
if (GotEqual)
{
element.Add(nextelement.Elements().Except(element.Elements(), new ElementComparer()));
skiptimes++;
}
else
{
break;
}
}
}
}
finalXML.Add(element);
}
This is horrible code, and I haven't had time to clean it up, but this works.
var xDoc = XDocument.Parse("<and><restriction type=\"Stopovers_Type\" Name=\"STP\"><NbMaxOfStops>4</NbMaxOfStops><Charges1><FirstAmount Currency=\"AUD\">10.00</FirstAmount></Charges1><StopoversSegs><GeoSpec><Location1>AKL</Location1><Location2>CHC</Location2></GeoSpec><ChargeIndicator>1</ChargeIndicator></StopoversSegs><StopoversSegs><GeoSpec><Location1>LAX</Location1><Location2>RAR</Location2></GeoSpec><ChargeIndicator>1</ChargeIndicator></StopoversSegs></restriction><restriction type=\"Stopovers_Type\" Name=\"STP\"><NbMaxOfStops>2</NbMaxOfStops><NbOfOutboundStops>1</NbOfOutboundStops><NbOfInboundStops>1</NbOfInboundStops><Charges1><FirstAmountNbOf>1</FirstAmountNbOf><FirstAmount Currency=\"AUD\">301.00</FirstAmount><AdditionalAmountNbOf>1</AdditionalAmountNbOf><AdditionalAmount Currency=\"AUD\">151.00</AdditionalAmount></Charges1><StopoversSegs><NbOfStops>1</NbOfStops><GeoSpec><Location1>LAX</Location1></GeoSpec><InboundOutboundIndicator>I</InboundOutboundIndicator><ChargeIndicator>1</ChargeIndicator></StopoversSegs></restriction><restriction type=\"Stopovers_Type\" Name=\"STP\"><NbMaxOfStops>2</NbMaxOfStops><NbOfOutboundStops>1</NbOfOutboundStops><NbOfInboundStops>1</NbOfInboundStops><Charges1><FirstAmountNbOf>1</FirstAmountNbOf><FirstAmount Currency=\"AUD\">301.00</FirstAmount><AdditionalAmountNbOf>1</AdditionalAmountNbOf><AdditionalAmount Currency=\"AUD\">151.00</AdditionalAmount></Charges1><StopoversSegs><NbOfStops>1</NbOfStops><GeoSpec><Location1>SFO</Location1></GeoSpec><InboundOutboundIndicator>O</InboundOutboundIndicator><ChargeIndicator>2</ChargeIndicator></StopoversSegs></restriction></and>");
var xDoc2 = XDocument.Parse("<and><restriction type=\"Stopovers_Type\" Name=\"STP\"><NbMaxOfStops>4</NbMaxOfStops><Charges1><FirstAmount Currency=\"AUD\">10.00</FirstAmount></Charges1><StopoversSegs><GeoSpec><Location1>AKL</Location1><Location2>CHC</Location2></GeoSpec><ChargeIndicator>1</ChargeIndicator></StopoversSegs><StopoversSegs><GeoSpec><Location1>LAX</Location1><Location2>RAR</Location2></GeoSpec><ChargeIndicator>1</ChargeIndicator></StopoversSegs></restriction><restriction type=\"Stopovers_Type\" Name=\"STP\"><NbMaxOfStops>2</NbMaxOfStops><NbOfOutboundStops>1</NbOfOutboundStops><NbOfInboundStops>1</NbOfInboundStops><Charges1><FirstAmountNbOf>1</FirstAmountNbOf><FirstAmount Currency=\"AUD\">301.00</FirstAmount><AdditionalAmountNbOf>1</AdditionalAmountNbOf><AdditionalAmount Currency=\"AUD\">151.00</AdditionalAmount></Charges1><StopoversSegs><NbOfStops>1</NbOfStops><GeoSpec><Location1>LAX</Location1></GeoSpec><InboundOutboundIndicator>I</InboundOutboundIndicator><ChargeIndicator>1</ChargeIndicator></StopoversSegs></restriction><restriction type=\"Stopovers_Type\" Name=\"STP\"><NbMaxOfStops>2</NbMaxOfStops><NbOfOutboundStops>1</NbOfOutboundStops><NbOfInboundStops>1</NbOfInboundStops><Charges1><FirstAmountNbOf>1</FirstAmountNbOf><FirstAmount Currency=\"AUD\">301.00</FirstAmount><AdditionalAmountNbOf>1</AdditionalAmountNbOf><AdditionalAmount Currency=\"AUD\">151.00</AdditionalAmount></Charges1><StopoversSegs><NbOfStops>1</NbOfStops><GeoSpec><Location1>SFO</Location1></GeoSpec><InboundOutboundIndicator>O</InboundOutboundIndicator><ChargeIndicator>2</ChargeIndicator></StopoversSegs></restriction></and>");
xDoc.Root.Descendants().Where(w => w.Name == "StopoversSegs").Remove();
var lsDistinct = xDoc.Root.Elements().Select(s => s.ToString()).Distinct().ToList();
var lDistinct = lsDistinct.Select(s => Tuple.Create(s, XElement.Parse(s))).ToList();
var lStopNodes = xDoc2.Root.Elements().Select(s => Tuple.Create(XElement.Parse(s.ToString()), XElement.Parse(s.ToString()))).ToList();
foreach ( var stopNode in lStopNodes)
{
stopNode.Item1.Descendants("StopoversSegs").Remove();
}
var lStopNodesToDistinct = lStopNodes.Select(s => Tuple.Create(s.Item1.ToString(), s.Item2.Descendants("StopoversSegs"))).ToList();
foreach (var distinct in lDistinct)
{
distinct.Item2.Add(lStopNodesToDistinct.Where(w => w.Item1 == distinct.Item1).Select(s => s.Item2.Nodes()).ToArray());
var test = lStopNodesToDistinct.Where(w => w.Item1 == distinct.Item1).Select(s => s.Item2.Nodes()).ToArray();
}
xDoc.Root.Elements().Remove();
xDoc.Root.Add(lDistinct.Select(s => s.Item2));
Related
I have a peace of xml which I need to gather information off.
<response>
<students>
<student>
<educationalinstitutionname>Test One</educationalinstitutionname>
<academicqualificationtype>TestLevel One</academicqualificationtype>
<starttime>22/02/06</starttime>
<endtime>19/08/10</endtime>
<grade>A</grade>
</student>
<student>
<educationalinstitutionname>Test One</educationalinstitutionname>
<academicqualificationtype>TestLevel Two</academicqualificationtype>
<starttime>22/02/06</starttime>
<endtime>19/08/10</endtime>
<grade>B</grade>
</student>
<student>
<educationalinstitutionname>Test Two</educationalinstitutionname>
<academicqualificationtype>TestLevel Two</academicqualificationtype>
<starttime>22/02/06</starttime>
<endtime>19/08/10</endtime>
<grade>C</grade>
</student>
<student>
<educationalinstitutionname>Test Two</educationalinstitutionname>
<academicqualificationtype>TestLevel Three</academicqualificationtype>
<starttime>22/02/06</starttime>
<endtime>19/08/10</endtime>
<grade>D</grade>
</student>
</students>
The issue which I am having is that I need to parse the educationalinstitutionname once if it the same but gather the academicqualificationtype, starttime, endtime and grade for everything that it appears under the same educationalinstitutionname node.
XmlNodeList WordXMLNodeLists = XmlResponceDoc.SelectNodes("/response/students/student");
string GetDetaisl = string.Empty;
foreach (XmlNode GradeItem in WordXMLNodeLists)
{
string GetStartDate = EducationElement.SelectNodes("/response/students/student/starttime")[0].InnerText;
string GetEndDate = EducationElement.SelectNodes("/response/students/student/endtime")[0].InnerText;
string GetEstablishmentName = EducationElement.GetElementsByTagName("educationalinstitutionname")[0].InnerText;
string GetGrade = EducationElement.SelectNodes("/response/students/student/grade")[0].InnerText;
GetDetaisl = string.Concat(GetStartDate, " ", GetEndDate, " ", GetEstablishmentName);
if (GetDetaisl.Length == GetEstablishmentName.Length)
{
string GetGrades = GetGrade;
GetEducationNodeDate.Add(GetGrades);
}
else
{
GetEducationNodeDate.Add(GetDetaisl);
continue;
}
}
The issue which I am finding is that I cannot seem to loop through the name once and gather all the information in the document under that name for the establishment. Thanks for any help which you can provide
It isn't very clear what you're trying to achieve specifically. I assumed that basically you want value of most of nodes within <student> tag :
XmlNodeList WordXMLNodeLists = XmlResponceDoc.SelectNodes("/response/students/student");
foreach (XmlNode GradeItem in WordXMLNodeLists)
{
string GetStartDate = GradeItem.SelectSingleNode("./starttime").InnerText;
string GetEndDate = GradeItem.SelectSingleNode("./endtime").InnerText;
string GetEstablishmentName = GradeItem.SelectSingleNode("./educationalinstitutionname").InnerText;
string GetGrade = GradeItem.SelectSingleNode("./grade").InnerText;
//do something with data gathered above
}
I am attempting to get the value of a specific node for each parent element found.
In the example I want to return each students First Name.
Instead I am getting the first elements name in each instance. The InnerText of a Student is correct but the InnerText of FirstName is always Alex.
var xml = #"<School>
<Students>
<Student>
<FirstName>Alex</FirstName>
<LastName>Smith</LastName>
<Grade>11</Grade>
</Student>
<Student>
<FirstName>Joanne</FirstName>
<LastName>Robins</LastName>
<Grade>12</Grade>
</Student>
<Student>
<FirstName>Steve</FirstName>
<LastName>Baker</LastName>
<Grade>11</Grade>
</Student>
</Students>
<Teachers>
<Teacher>
<FirstName>George</FirstName>
<LastName>Roberts</LastName>
<Grade>11</Grade>
</Teacher>
<Teacher>
<FirstName>Amanda</FirstName>
<LastName>Walker</LastName>
<Grade>12</Grade>
</Teacher>
<Teacher>
<FirstName>Tracey</FirstName>
<LastName>Smith</LastName>
<Grade>12</Grade>
</Teacher>
</Teachers>
</School>";
var doc = new XmlDocument();
doc.LoadXml(xml);
var resourceTypeNodes = doc.GetElementsByTagName("Student");
var resourceTypesIterator = resourceTypeNodes.GetEnumerator();
while (resourceTypesIterator != null && resourceTypesIterator.MoveNext())
{
var resourceTypeNode = resourceTypesIterator.Current as XmlNode;
var typeNameElement = resourceTypeNode.SelectSingleNode("//FirstName");
Console.WriteLine(resourceTypeNode.InnerXml);
Console.WriteLine(typeNameElement.InnerText);
}
This is the output of the above code.
<FirstName>Alex</FirstName><LastName>Smith</LastName><Grade>11</Grade>
Alex
<FirstName>Joanne</FirstName><LastName>Robins</LastName><Grade>12</Grade>
Alex
<FirstName>Steve</FirstName><LastName>Baker</LastName><Grade>11</Grade>
Alex
What am I missing?
Because you're using //FirstName XPath expression, that will always return first node from root, it doesn't matter if you invoke on children. Just change this:
var typeNameElement = resourceTypeNode.SelectSingleNode("//FirstName");
To this:
var typeNameElement = resourceTypeNode.SelectSingleNode("FirstName");
Moreover is there any specific reason you're manually using IEnumerator? You may simplify your code with foreach:
foreach (XmlNode resourceTypeNode in doc.GetElementsByTagName("Student"))
{
var typeNameElement = resourceTypeNode.SelectSingleNode("FirstName");
Console.WriteLine(resourceTypeNode.InnerXml);
Console.WriteLine(typeNameElement.InnerText);
}
How can I add new node where id is a certain value from a textbox?
This is my xml:
<Students>
<Student>
<id>111</id>
<Value>1</Value>
<information>
<data DateTime="02.04.2014 13:00:00" Value="1"/>
</information>
</Student>
</Students>
So my question is, I have a textbox where I enter the student id. After clicking on a button, I want to add a new node in information node, containing the attribute date and time in that moment.
Another thing is that I want the innerText in the node to be changed from 1 to 0 and vice-versa each new time I click. So that will be the second attribute in the node.
The next time I click, it suppose to add a new node, it will add this.
<information>
<data DateTime="02.04.2014 13:00:00" Value="1"/>
<data DateTime="02.04.2014 14:00:00" Value="0"/>
</information>
How can I do it with XmlLinq?
var xdoc = XDocument.Load(path_to_xml);
var student = xdoc.Root.Elements("Student")
.FirstOrDefault(s => (int)s.Element("id") == id);
if (student != null) // check if student was found
{
var info = student.Element("information");
if (info == null) // check if it has information element
{
info = new XElement("information");
student.Add(info); // create and add information element
}
var lastValue = info.Elements("data")
.OrderBy(d => (DateTime)d.Attribute("DateTime"))
.Select(d => (int)d.Attribute("Value"))
.LastOrDefault(); // get latest value
// create new data element
var data =
new XElement("data",
new XAttribute("DateTime", DateTime.Now.ToString("MM.dd.yyyy HH:mm:ss")),
new XAttribute("Value", lastValue == 0 ? 1 : 0));
info.Add(data); // add data element to information element
xdoc.Save(path_to_xml); // save file
}
Result:
<Students>
<Student>
<id>111</id>
<Value>1</Value>
<information>
<data DateTime="02.04.2014 13:00:00" Value="1" />
<data DateTime="02.05.2014 00:40:18" Value="0" />
</information>
</Student>
</Students>
c# has a method that will let you do this easily:
XmlNode.InsertAfter Method
Link to the actual page: http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.insertafter(v=vs.110).aspx
Code examples if you don't want to click through:
using System;
using System.IO;
using System.Xml;
public class Sample {
public static void Main() {
XmlDocument doc = new XmlDocument();
doc.LoadXml("<book genre='novel' ISBN='1-861001-57-5'>" +
"<title>Pride And Prejudice</title>" +
"</book>");
XmlNode root = doc.DocumentElement;
//Create a new node.
XmlElement elem = doc.CreateElement("price");
elem.InnerText="19.95";
//Add the node to the document.
root.InsertAfter(elem, root.FirstChild);
Console.WriteLine("Display the modified XML...");
doc.Save(Console.Out);
}
}
If you need to find a specifc node to insert after, check this out
http://msdn.microsoft.com/en-us/library/h0hw012b(v=vs.110).aspx
I have an XML file
<Person>
<PersonItem id="0">
<Time>1/8/2014</Time>
<Step><![CDATA[Normal]]></Step>
<HasAddress/>
<Address/>
</PersonItem>
<PersonItem id="1">
<Time>1/8/2014 3:21:45 PM</Time>
<Step><![CDATA[Normal]]></Step>
<HasAddress/>
<Address/>
</PersonItem>
<PersonItem id="2">
<Time>1/8/2014</Time>
<Step><![CDATA[Normal]]></Step>
<HasAddress>Main</HasAddress>
<Address>
<AddressItem id="0" location=5>
<Address>15 Oak</Address>
</AddressItem>
<AddressItem id="1" location=7>
<Address>12 Maple</Address>
</AddressItem>
<AddressItem id="2" location=8>
<Address>30 Beech</Picture>
</AddressItem>
</Address>
</PersonItem>
</Person>
I want to put to retrieve the information and send some of it to a database. I've tried several different ways of dealing with this and I believe I'm close. Here is the Linq I tried.
public void DoIt(fileName)
{
XElement xml = XElement.Load(fileName);
var items = from item in xml.Elements("PersonItem")
where (from x in item.Elements("HasAddress")
where x.Element("HasAddress") != null
select x).Any()
select item;
Array.ForEach(items.ToArray(),
o=>Console.WriteLine(o.Element("Time").Value));
Console.ReadLine();
}
The problem is nothing is being returned.
Could be just a typo but in your xml file there is this tag error.
<Address>30 Beech</Picture>
which should be:
<Address>30 Beech</Address>
Try this:
XElement xml = XElement.Load(fileName);
var items = xml.Descendants("PersonItem")
.Where(x => (string)x.Element("HasAddress") != null)
.Select(x => x);
XDocument xml = XDocument.Load("Input.xml");
var items = from item in xml.Root.Elements("PersonItem")
where !string.IsNullOrEmpty((string)item.Element("HasAddress"))
select item;
For your sample XML document returns only the last PersonItem element.
Using a loop with xelement class in C# i would like to get the below result!
<data>
<description>Cities that I have recently visited.</description>
<cities>
<city id="1">
<name>Chicago1</name>
<state>IN1</state>
</city>
<city id="2">
<name>Chicago2</name>
<state>IN2</state>
</city>
<city id="3">
<name>Chicago3</name>
<state>IN3</state>
</city>
</cities>
</data>
This is the code i have tried so far! any help?? i need to use a loop and get the above values..The loop i used is commented..
namespace ConsoleApplication13
{
class Program
{
static void Main(string[] args)
{
XElement xmlDataStore = new XElement("data",
new XElement("cities",
new XElement("city", new XAttribute("id", "1")),
new XElement("city", "Colombo"),
new XElement("name", "lname"),
new XElement("state", "0772569984")
)
)
;
//var list = from x in XElement.ReadFrom(xmlDataStore).Element("Node").Elements()
//select new
//{
// Name = x.Name,
// Value = (string)x
//};
Console.WriteLine(xmlDataStore);
Console.ReadLine();
}
}
}
What i get...
<cities>
<city id="1">
<name>Chicago1</name>
<state>IN1</state>
</city>
</cities>
What i want...
<data>
<description>Cities that I have recently visited.</description>
<cities>
<city id="1">
<name>Chicago1</name>
<state>IN1</state>
</city>
<city id="2">
<name>Chicago2</name>
<state>IN2</state>
</city>
<city id="3">
<name>Chicago3</name>
<state>IN3</state>
</city>
</cities>
</data>
Okay, I still don't exactly know what the real problem is, so let's start with this:
private static void citiesXml()
{
const string desc = "Cities that I have recently visited.";
// set up a list of all the different cities
var list = new List<Tuple<string, string>>();
list.Add(new Tuple<string, string>("Chicago1", "IN1"));
list.Add(new Tuple<string, string>("Chicago2", "IN2"));
list.Add(new Tuple<string, string>("Chicago3", "IN3"));
var xmlDataStore = new XElement("data", new XElement("description", desc));
var xmlCities = new XElement("cities");
// loop through the list of cities and create a XElement for each single one
for (var i = 0; i < list.Count; i++)
{
xmlCities.Add(new XElement("city",
new XAttribute("id", i + 1),
new XElement("name", list[i].Item1),
new XElement("state", list[i].Item2)));
}
// add the cities to the data store object
xmlDataStore.Add(xmlCities);
Console.WriteLine(xmlDataStore);
Console.ReadLine();
}
This will print:
<data>
<description>Cities that I have recently visited.</description>
<cities>
<city id="1">
<name>Chicago1</name>
<state>IN1</state>
</city>
<city id="2">
<name>Chicago2</name>
<state>IN2</state>
</city>
<city id="3">
<name>Chicago3</name>
<state>IN3</state>
</city>
</cities>
</data>
So as far as I see it, the only difference is that there are no blank lines between cities. Are the missing blank lines the problem?
Check by Descendants
XDocument xdoc = XDocument.Load("Xml File Path"); //save that xml in "C:\test.xml "
IEnumerable<XElement> xEle = xdoc.XPathSelectElements("//cities");
if(xEle !=null)
{
foreach(XElement xE in Xelement.Descendants())
{
// here you will get everything ......
}
}
you can loop through