Convert a XML to CSV using C# - c#

How to convert a XML file to a CSV file in C#, showing only these Tags: <original-impot-no>, <price>, <Small-price>, <Big-price>?
sample XML code: it represents one line among several lines, and every line may contains several <product-lineitem>Tags
<?xml version="1.0" encoding="UTF-8"?>
<impots xmlns="http://www.google.com/xml/impot//20016-02-31">
<impot impot-no="W0110891258">
<impot-date>2017-12-10T22:33:35.000Z</impot-date>
<prop-by>Yallo</prop-by>
<original-impot-no>891258</original-impot-no>
<currency>EUR</currency>
<server-locale>Esp</server-locale>
<lax>gross</lax>
<current-impot-no>123358</current-impot-no>
<product-lineitems>
<product-lineitem>
<price>450</price>
<red>6.50</red>
<Small-price>39</Small-price>
<Big-price>3229</Big-price>
<lineitem-text>Grand create</lineitem-text>
<basis>234.00</basis>
</product-lineitem>
</product-lineitems>
<product-lineitem>
<price>432</price>
<red>12</red>
<Small-price>44</Small-price>
<Big-price>34</Big-price>
<lineitem-text>Small create</lineitem-text>
<basis>44.00</basis>
</product-lineitem>
</product-lineitems>
</impot>
</impots>
I should get someting like this in y CSV file:
891258;450;39;229
891258;432;44;34
the C# code:
the problem that Im facing with this code is that I can not retrieve the descandent of the TAG <impot>
XmlTextReader xtr = new XmlTextReader(#"C:\Temp_Convert\Impot.xml");
StringBuilder dataToBeWritten = new StringBuilder();
while (xtr.Read())
{
if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "original-impot-no")
{
string s1 = xtr.ReadElementString();
dataToBeWritten.Append(s1);
dataToBeWritten.Append(";");
}
else if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "price")
{
string s2 = xtr.ReadElementString();
dataToBeWritten.Append(s2);
dataToBeWritten.Append(";");
}
else if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "Small-price")
{
string s2 = xtr.ReadElementString();
dataToBeWritten.Append(s2);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
else if (xtr.NodeType == XmlNodeType.Element && xtr.Name == "Big-price")
{
string s2 = xtr.ReadElementString();
dataToBeWritten.Append(s2);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
}
File.WriteAllText(#"C:\Temp_Convert\Impot.csv", dataToBeWritten.ToString());
}
Can somebody please propose a solution, thank you so much in advance.

You have an invalid XML. I guess here is the correct format.
<?xml version="1.0" encoding="UTF-8"?>
<impots xmlns="http://www.google.com/xml/impot//20016-02-31">
<impot impot-no="W0110891258">
<impot-date>2017-12-10T22:33:35.000Z</impot-date>
<prop-by>Yallo</prop-by>
<original-impot-no>891258</original-impot-no>
<currency>EUR</currency>
<server-locale>Esp</server-locale>
<lax>gross</lax>
<current-impot-no>123358</current-impot-no>
<product-lineitems>
<product-lineitem>
<price>450</price>
<red>6.50</red>
<Small-price>39.00</Small-price>
<Big-price>3229.00</Big-price>
<lineitem-text>Grand create</lineitem-text>
<basis>234.00</basis>
</product-lineitem>
</product-lineitems>
<product-lineitems>
<product-lineitem>
<price>432</price>
<red>12</red>
<Small-price>44.00</Small-price>
<Big-price>34.00</Big-price>
<lineitem-text>Small create</lineitem-text>
<basis>44.00</basis>
</product-lineitem>
</product-lineitems>
</impot>
</impots>
You couldn't retrieve the descendant because you are not including the namespace http://www.google.com/xml/impot//20016-02-31.
Here is how the code should be.
XNamespace ns = "http://www.google.com/xml/impot//20016-02-31";
var results = xDocument.Descendants(ns + "impot");
Then, you need to modify your query to retrieve elements that you need.
Here is the sample. I assume that product-lineitems only has one child product-lineitem.
var results = xDocument.Descendants(ns + "impot").Select(x => new {
ImpotNo = x.Attribute("impot-no")?.Value,
ProductLineItems = x.Descendants(ns + "product-lineitems").Select(y => new
{
Item = y.Descendants(ns + "product-lineitem").Select(z => new
{
Price = z.Element(ns + "price")?.Value,
SmallPrice = z.Element(ns + "Small-price")?.Value,
BigPrice = z.Element(ns + "Big-price")?.Value,
}).FirstOrDefault()
})
});
foreach (var result in results)
{
foreach (var productLine in result.ProductLineItems)
{
dataToBeWritten.Append(result.ImpotNo);
dataToBeWritten.Append(";");
dataToBeWritten.Append(productLine.Item.Price);
dataToBeWritten.Append(";");
dataToBeWritten.Append(productLine.Item.SmallPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(productLine.Item.BigPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
}

Well, first I tried to reformat your XML to be a bit more readable but the tag structure still seems wrong...
<impots
xmlns="http://www.google.com/xml/impot//20016-02-31">
<impot impot-no="W0110891258">
<impot-date>2017-12-10T22:33:35.000Z</impot-date>
<prop-by>Yallo</prop-by>
<original-impot-no>891258</original-impot-no>
<currency>EUR</currency>
<server-locale>Esp</server-locale>
<lax>gross</lax>
<current-impot-no>123358</current-impot-no>
<product-lineitems>
<product-lineitem>
<price>450</price>
<red>6.50</red>
<Small-price>39.00</Small-price>
<Big-price>3229.00</Big-price>
<lineitem-text>Grand create</lineitem-text>
<basis>234.00</basis>
-
</product-lineitems>
-
</product-lineitem>
<product-lineitems>
<product-lineitem>
<price>432</price>
<red>12</red>
<Small-price>44.00</Small-price>
<Big-price>34.00</Big-price>
<lineitem-text>Small create</lineitem-text>
<basis>44.00</basis>
</product-lineitems>
</product-lineitem>
Nonetheless, I'm guessing this line is incorrect since "impot-no" is an attribute...
impot-no = (string)x.Element("impot impot-no")
Perhaps you meant that line to be...
impot-no = (string)x.Attribute("impot-no").Value
Going by memory -- hopefully that is the correct way to retrieve an attribute.

Review the following code. Note the use of SelectMany to get the impot items to construct the desired object models.
XNamespace ns = "http://www.google.com/xml/impot//20016-02-31";
var results = xDocument.Descendants(ns + "impot")
.SelectMany(impot => impot.Descendants(impot.Name.Namespace + "product-lineitem")
.Select(item => new {
ImpotNo = (string)impot.Element(impot.Name.Namespace + "original-impot-no"),
Price = (string)item.Element(item.Name.Namespace + "price"),
SmallPrice = (string)item.Element(item.Name.Namespace + "Small-price"),
BigPrice = (string)item.Element(item.Name.Namespace + "Big-price"),
})
).ToList();
for (int i = 0; i < results.Count; i++) {
dataToBeWritten.Append(results[i].ImpotNo);
dataToBeWritten.Append(";");
dataToBeWritten.Append(results[i].Price);
dataToBeWritten.Append(";");
dataToBeWritten.Append(results[i].SmallPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(results[i].BigPrice);
dataToBeWritten.Append(";");
dataToBeWritten.Append(0);
dataToBeWritten.Append(Environment.NewLine);
}
Also note the syntax used for the properties.

Related

Add new XEelement and Update XML.Descendent

I have this code that works well, but I can't seem to figure out how to actually save the update XML.descendents list = JobNames to the XML document.
public void ProcessJobNames(String JobName = null, Boolean rRemove = false)
{
System.Xml.Linq.XDocument xml = System.Xml.Linq.XDocument.Load(_JobPath + #"\" + _ConfigFile);
var JobNames = xml.Descendants("setting").Where(xe => xe.Attribute("key").Value.Contains("JobType")).ToList();
System.Xml.Linq.XElement copyXE = null;
int JobCount = 0;
foreach (System.Xml.Linq.XElement strJobName in JobNames)
{
//strJobNames.Add(strJobName.LastAttribute.Value.ToString());
JobCount = JobCount + 1;
strJobName.FirstAttribute.Value = "JobType" + (JobCount).ToString();
copyXE = strJobName; // Make a copy of the Job
// Check if the JobName is in the Job Processor to be Removed
if ((JobName != null) && (rRemove == true) && (strJobName.LastAttribute.Value.ToString().Contains(JobName)))
{
// Remove the Job from the Configuration File
strJobName.Remove();
JobCount = JobCount - 1;
}
// Check if the JobName is in the Job Processor to be Added
if ((JobName != null) && (rRemove == false) && (strJobName.LastAttribute.Value.ToString().Contains(JobName)))
{
// Add the Job from the Configuration File
strJobName.Remove();
JobCount = JobCount - 1;
}
}
// Add the new Job
if ((JobName != null) && (rRemove == false)) {
File.Copy(_RulesFolder + cmboRules.Text, _JobPath + cmboRules.Text);
System.Xml.Linq.XElement newJob = new System.Xml.Linq.XElement(copyXE);
newJob.FirstAttribute.Value = "JobType" + (JobCount + 1).ToString();
newJob.LastAttribute.Value = _JobPrefix + cmboRules.Text;
copyXE.Parent.Add(newJob)
}
// Save the XML Configuration File
xml.Save(_JobPath + _ConfigFile);
}
I think I'm missing something after "JobNames.Add(newJob)" to update the descendants in the XML document.
To add a node in runtime, You could use XElement.Parent.Add()method to add child. For example,
foreach(var job in jobList)
{
if(job.Attribute("name").Value == "job1") //Any condition on which new node is to be added
{
var copy = job;
copy.Attribute("name").Value = "job3";
job.Parent.Add(copy);
}
}
Sample
Input
<root>
<jobs>
<job name="job1"></job>
<job name="job2"></job>
</jobs>
</root>
Output
<root>
<jobs>
<job name="job3"></job>
<job name="job2"></job>
<job name="job3"></job>
</jobs>
</root>

How to get separate values from Xlement

This is a portion of XML I'm trying to parse
<BRTHDATES>
<BRTHDATE value="5/1/1963" code="B"/>
</BRTHDATES>
var birthdates = xmlDoc.XPathSelectElements("/INDV/PERSON/BRTHDATES").Elements().Where(e => e.Name == "BRTHDATE");
xe = birthdates.Elements().Where(e => e.Name == "BRTHDATE");
bbs = from b in birthdates
select new
{
Birthdays = b.FirstAttribute.Value,
Code = b?.Value
};
var status = birthdates.Elements().Where(e => e.Name.LocalName == "BRTHDATE").Single().Value;
When I try to get "Value" from the Element I get an empty string. I can't get anything for the "code" attribute.
It sure seems like this should be a lot easier...
You can try below code. I've already tested through a test project and got the require value.
string personBirthday = string.Empty;
string soapResult = #"<?xml version=""1.0"" encoding=""utf - 8"" ?><INDV> <PERSON> <BRTHDATES><BRTHDATE value = ""5/1/1963"" code = ""B"" /> </BRTHDATES></PERSON></INDV> ";
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(soapResult));
XmlNodeList person = doc.GetElementsByTagName("BRTHDATES");
if (person[0].ChildNodes.Count > 0)
{
foreach (XmlNode item in person[0].ChildNodes)
{
if (item.Name.Trim().Equals("BRTHDATE"))
{
personBirthday = !string.IsNullOrEmpty(item.Attributes[0].Value) ? item.Attributes[0].Value.Trim() : string.Empty;
}
}
}
Here is the solution
You can select specific Element from a Xml. Just try below sample code
XmlNodeList generalTabNodeList = xmlDocument.SelectNodes("/INDV/PERSON/BRTHDATES");
foreach (XmlNode node in generalTabNodeList)
{
if (node.ChildNodes.Count > 0)
{
string birthdate = !string.IsNullOrEmpty(node.ChildNodes[0].ToString()) ? node.ChildNodes[2].InnerText.ToString() : string.Empty;
}
}
I can't quite follow what you are trying to do, but, this should get you going:
For an XML file that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<INDV>
<PERSON>
<BRTHDATES>
<BRTHDATE value="5/1/1963" code="B"/>
</BRTHDATES>
</PERSON>
</INDV>
(Note, this is an entire XML document - one that matches your code, not just the snippet you provided (that doesn't match your code))
This code will pick out the value and code attributes:
using (var xmlStream = new FileStream("Test.xml", FileMode.Open))
{
XDocument xmlDocument = XDocument.Load(xmlStream);
var birthDateElements = xmlDocument.XPathSelectElements("/INDV/PERSON/BRTHDATES/BRTHDATE");
var birthDateElement = birthDateElements.FirstOrDefault();
if (birthDateElement != null)
{
var attributes = birthDateElement.Attributes();
var valueAttribute = attributes.Where(a => a.Name == "value");
var codeAttribute = attributes.Where(a => a.Name == "code");
}
}
You can play around with this code to figure out what you want to do. Whatever you do, don't pick out attributes by position, pick them out by name.

Read XML to a list<string[]> using xmlread

I am going to read an xml file of a gps points to a list and later convert them to a polygon and save in database.
Here is my problem..When I try to read file every item in list is the same as others..can you help me with my problem?thanks in advanced
here is my code:
public List<string[]> loadXML(string xmlpath)
{
List<string[]> points = new List<string[]>();
XmlReader xmlReader = XmlReader.Create(xmlpath);
while (xmlReader.Read())
{
string[] item = new string[3];
if ((xmlReader.NodeType == XmlNodeType.Element) && (xmlReader.Name == "trkpt"))
{
if (xmlReader.HasAttributes)
{
item[0] = xmlReader.GetAttribute("lat");
item[1] = xmlReader.GetAttribute("lon");
}
}
if ((xmlReader.NodeType == XmlNodeType.Element) && (xmlReader.Name == "ele"))
{
item[2] = xmlReader.ReadInnerXml();
}
points.Add(item);
}
return points;
}
and here is a peice of my xml:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="" xmlns:gpxx="" xmlns:wptx1="" xmlns:gpxtpx="" creator="GPSMAP 78s" version="1.1" xmlns:xsi="" xsi:schemaLocation="">
<metadata>
<link href=""><text>Garmin International</text></link>
<time>2014-06-18T13:55:07Z</time></metadata>
<trk><name>PV164B2KHE</name>
<extensions><gpxx:TrackExtension><gpxx:DisplayColor>Cyan</gpxx:DisplayColor></gpxx:TrackExtension></extensions>
<trkseg>
<trkpt lat="31.0403929744" lon="51.5264290944"><ele>2089.08</ele><time>2014-06-18T13:45:57Z</time></trkpt>
<trkpt lat="31.0403881129" lon="51.5264252387"><ele>2088.60</ele><time>2014-06-18T13:46:01Z</time></trkpt></trkseg></trk></gpx>
I want to extract lat,lon and ele for each point
I would use Linq to XML for that:
var xmlDocument = XDocument.Load("path");
XNamepsace ns = xmlDocument.Root.GetDefaultNamespace();
var values = xmlDocument.Root
.Descendants(ns + "trkpt")
.Select(x => new
{
lat = (string)x.Attribute("lat"),
lon = (string)x.Attribute("lon"),
ele = (string)x.Element(ns + "ele")
});
Your mistake is xmlReader.Read() reads the next node, but you are creating a string[] array for each node.A node can not be a trkpt and ele at the same time. So that will never give you the desired result.

XML read text inside two elements

Here is my xml code:
<?xml version="1.0" encoding="utf-8"?>
<updater>
<version>1.0.7</version>
<Enabled>true</Enabled>
<item>
<url>some url</url>
<name>file name</name>
</item>
<item>
<url>other url</url>
<name>other file name</name>
</item>
</updater>
how can i get the value of url and name inside of both item elements? The full code have 9 elements with the name item. Please make the solution fit with this code:
XmlTextReader reader = null;
try
{
string xmlURL = "someurl";
reader = new XmlTextReader(xmlURL);
reader.MoveToContent();
string elementName = "";
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "updater"))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element) elementName = reader.Name;
else
{
if ((reader.NodeType == XmlNodeType.Text) && (reader.HasValue))
{
switch (elementName)
{
case "url":
if (nummer >= urls.Length)
Array.Resize(ref urls, urls.Length + 1);
urls[nummer] = reader.Value.ToString();
MessageBox.Show(urls[nummer]);
break;
case "name":
if (nummer >= names.Length)
Array.Resize(ref names, names.Length + 1);
names[nummer] = reader.Value.ToString();
MessageBox.Show(names[nummer]);
break;
}
nummer++;
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
Any help will be appreciated. thanks in advance.
PS. If i'm unclear somewhere, or if you need more information then just explain what's needed.
You can use LINQ to XML:
var xdoc = XDocument.Load(path_to_xml);
var items = from i in xdoc.Root.Elements("item")
select new {
Url = (string)i.Element("url"),
Name = (string)i.Element("name")
};
This will give list of anonymous objects corresponding to your item elements. Each object will have strongly-typed properties for url and name:
foreach(var item in items)
{
// use item.Url or item.Name
}
XDocument doc = XDocument.Load("Xml.xml");
IEnumerable<XElement> items = doc.Descendants("updater").Elements("item")
.Select(x => new { Url = x.Element("url").Value,
Name = x.Element("name").Value });

Locate nodes in XML file

I have fixed my issue with reading the XML files. What I am needing now is to trim the datetime down to just MM dd yyyy hh:mm:ss and not bring the rest of it over when I insert into my Informix database.
This is the XML info:
<RecordFilingRequestMessage xmlns:nc="http://niem.gov/niem/niem-core/2.0">
<nc:DocumentIdentification>
<nc:IdentificationID>3212842</nc:IdentificationID>
</nc:DocumentIdentification>
<nc:DocumentPostDate>
<nc:DateTime>2013-06-25T11:32:08.5343733-04:00</nc:DateTime>
</nc:DocumentPostDate>
<nc:DocumentSubmitter>
<ecf:EntityPerson s:id="REVIEWER">
<nc:PersonName />
<nc:PersonOtherIdentification>
<nc:IdentificationID>41130</nc:IdentificationID>
<nc:IdentificationCategoryText>FLEPORTAL</nc:IdentificationCategoryText>
</nc:PersonOtherIdentification>
<nc:PersonOtherIdentification>
<nc:IdentificationID>kacolburn</nc:IdentificationID>
<nc:IdentificationCategoryText>FLEPORTAL_LOGONNAME</nc:IdentificationCategoryText>
</nc:PersonOtherIdentification>
...and here is my C# code:
string DocID = null;
int elementCount = 0;
string reqID = null;
string reqDateTime = null;
string empName = null;
string[] fileEntries = Directory.GetFiles(#"C:\XML\3212842.xml");
foreach (string fileName in fileEntries)
{
XmlReader xr = XmlReader.Create(fileName); //reads XML from folder
while (xr.Read())
{
if (xr.NodeType == XmlNodeType.Element && xr.Name == "nc:DateTime")
{
reqDateTime = xr.ReadElementContentAsString();
}
if (xr.NodeType == XmlNodeType.Element && xr.Name == "nc:IdentificationID")
{
elementCount++;
DocID = xr.ReadElementContentAsString();
if (elementCount == 1)
{
reqID = DocID;
}
if (elementCount == 3)
{
empName = DocID;
listBox1.Items.Add(reqID + " / " + reqDateTime + " / " + empName);
elementCount = 0;
break;
}
My first thought is, that the last '/' doesn't belong to the 'SelectNodes' call.
Alternatively, this code will solve your problem:
foreach(XmlNode node in xmlDoc.GetElementsByTagName("RecordFilingRequest")[0].GetElementsByTagName("nc:DocumentIdentification"))
{
int ID = Convert.ToInt32(node.FirstChild().InnerText);
}
Edit: This does assume, that 'RecordFilingRequest' always exists. Add a try .. catch statement, if that isn't the case.
Looks like the issue is that the XML uses namespaces, and your XPath does not. You didn't post your full XML, but you probably have something like xmlns:nc="http://some.url/ in there. Make sure to include a the namespace in a namespace manager, then add namespace prefixes to your query:
var nameTable = new NameTable();
var nsMgr = new XmlNamespaceManager(nameTable);
nsmgr.AddNamespace("nc", "http://some.url/");
var dataNodes = xmlDoc.SelectNodes("nc:RecordFilingRequest/nc:DocumentIdentification", nsMgr);
foreach (var node in dataNodes)
{
var ID = Convert.ToInt32(node.SelectSingleNode("nc:IdentificationID", nsMgr).InnerText);
// insert into database, e.g. using SqlCommand or whatever
}

Categories

Resources