How to read XML using Linq with Nested Descendants - c#

How to read the elements inside nested descendants in LINQ
My Xml file format
<SmPpProperties>
<PpProperties>
<Project Name="CEESI all 2" CreatedDate="2013-11-18T16:48:54.9452592+01:00" ModifiedDate="2013-11-18T16:48:57.2413905+01:00">
<NuclearSystem>Barium</NuclearSystem>
<TimeLine>
<Clip>
<FilePathFP>FPS\FP001D\Default-wp000-15Oct2012-105001.vxbin</FilePathFP>
</Clip>
</TimeLine>
</Project>
</PpProperties>
</SmPpProperties>
I am trying to use C # code as
var SmPpProperties
= from PpProperties in xmldoc.Descendants("PpProperties")
select new
{
from Project in xmldoc.Descendants("Project ")
select new {
*How to proceed*

var SmPpProperties = from poj in xmldoc.Descendants("Project")
select new {name = poj.Attribute("Name"),
filePath = poj.Element("TimeLine").Element("Clip").Element("FilePathFP").Value};
foreach (var item in SmPpProperties)
{
Console.WriteLine("Name = {0}, File Path = {1}", item.name, item.filePath);
}

Related

C# XML tags to csv

I have dozend of XML filed in the following format (short sample):
<namespace name="Colors">
<green>
<en>Green</en>
<de>Gruen</de>
</green>
<blue>
<en>Blue</en>
<de>Blau</de>
</blue>
<namespace name="Subcolors">
<perlwhite>
<en>Perl White</en>
<de>Perlweis</de>
</perlwhite>
<racingblack>
<en>Racing Black</en>
<de>Rennschwarz</de>
</racingblack>
</namespace>
</namespace>
And i have to extract all the language tags and output them into a csv file in this format:
en;de;
Green;Gruen;
Blue;Blau;
Perl White;PerlweiƟ;
Racing Black;Renn Schwarz;
Then, I give this CSV file away for translation. After translation, there is a new language added to the CSV file, french for example:
en;de;fr;
Green;Gruen;Vert;
Blue;Blau;Bleu;
Perl White;PerlweiƟ;Perl Blanc;
Racing Black;Rennschwarz;Courses Noir;
Then i need to read this csv file in again, and append all tags to all the corresponding xml-files, like so:
<namespace name="Colors">
<green>
<en>Green</en>
<de>Gruen</de>
<fr>Vert</fr>
</green>
<blue>
<en>Blue</en>
<de>Blau</de>
<fr>Bleu</fr>
</blue>
<namespace name="Subcolors">
<perlwhite>
<en>Perl White</en>
<de>Perlweis</de>
<fr>Perl Blanc</fr>
</perlwhite>
<racingblack>
<en>Racing Black</en>
<de>Renn Schwarz</de>
<fr>Courses Noir</fr>
</racingblack>
</namespace>
</namespace>
And namespaces or other nodes(not listed here, like "multicolor", "colored" and many more) can be nested more then once. Not every file contains every node. And not every node is nested the same way in each xml file. Thats different from file to file. But at the end, each branch ends with couple of language tags. And these need to be read and updated.
So an xml file can look like this:
<namespace name="Colors">
<green>
<en>Green</en>
<de>Gruen</de>
</green>
<blue>
<en>Blue</en>
<de>Blau</de>
</blue>
<namespace name="Subcolors">
<perlwhite>
<en>Perl White</en>
<de>Perlweis</de>
</perlwhite>
<racingblack>
<en>Racing Black</en>
<de>Rennschwarz</de>
</racingblack>
<colored>
<namespace name="Misc">
<fruits>
<apple>
<de>Apfel</de>
<en>Apple</en>
</apple>
<orange>
<de>Orange</de>
<en>Orange</en>
</orange>
</fruits>
<vegetables>
<cucumber>
<en>Cucumber</en>
<de>Gurke</de>
</cucumber>
</vegetables>
<namespace name="Other">
<othertag>
<entry>
<en>Entry</en>
<de>Eintrag</de>
</entry>
</othertag>
</namespace>
</namespace>
</colored>
</namespace>
</namespace>
So not every xml file is the same, and there are different nodes with different tag names differenty nested. But every branch ends with language tags.
Can somebody help me to do this in a simple way with C#?. Maybe two simple functions like Import(readCsvPath, appendXmlPath) and Export(readXmLPath, writeCsvPath).
The code basically work with issues. The original xml has perlwhite, but the new csv has Perl-White. How do you know where to put the dash. I converted the small p to upper P but do not know where to put the dash.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication103
{
class Program
{
const string INPUT_XML = #"c:\temp\test.xml";
const string OUTPUT_CSV = #"c:\temp\test.csv";
const string INPUT_CSV = #"c:\temp\test2.csv";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(INPUT_XML);
var colorsWithDuplicates = doc.Descendants("namespace")
.SelectMany(ns => ns.Elements()
.SelectMany(color => color.Elements().Select(y => new {color = color.Name.LocalName, language = y.Name.LocalName, value = (string)y}))
).ToList();
var colors = colorsWithDuplicates.GroupBy(x => new object[] { x.color, x.language }).Select(x => x.First()).ToList();
var sortedAndGrouped = colors.OrderBy(x => x.language).ThenBy(x => x.color).GroupBy(x => x.color).ToList();
List<string> countries = sortedAndGrouped.FirstOrDefault().Select(x => x.language).ToList();
StreamWriter writer = new StreamWriter(OUTPUT_CSV, false, Encoding.Unicode);
writer.WriteLine(string.Join(",",countries));
foreach (var color in sortedAndGrouped)
{
writer.WriteLine(string.Join(";",color.Select(x => x.value)));
}
writer.Flush();
writer.Close();
StreamReader reader = new StreamReader(INPUT_CSV);
List<string> newCountries = reader.ReadLine().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
string line = "";
Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
List<string> splitLine = line.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
dict.Add(splitLine[0], splitLine);
}
//now replace colors
foreach (XElement xNs in doc.Descendants("namespace"))
{
string name = (string)xNs.Attribute("name");
if((name == "Colors") || (name == "Subcolors"))
{
foreach (XElement xColor in xNs.Elements())
{
if (xColor.Name.LocalName != "namespace")
{
string checkColor = xColor.Name.LocalName;
checkColor = (string)xColor.Element("en"); // use english name
if (checkColor != null)
{
List<string> inputColors = dict[checkColor];
for (int index = 0; index < inputColors.Count; index++)
{
XElement country = xColor.Element(newCountries[index]);
if (country == null)
{
xColor.Add(new XElement(newCountries[index], inputColors[index]));
}
}
}
}
}
}
else
{
foreach (XElement group in xNs.Elements())
{
foreach(XElement xColor in group.Elements())
{
string checkColor = xColor.Name.LocalName;
checkColor = char.ToUpper(checkColor[0]) + checkColor.Substring(1);
if (checkColor != null)
{
List<string> inputColors = dict[checkColor];
for (int index = 0; index < inputColors.Count; index++)
{
XElement country = xColor.Element(newCountries[index]);
if (country == null)
{
xColor.Add(new XElement(newCountries[index], inputColors[index]));
}
}
}
}
}
}
}
}
}
}
For importing an XML to CSV file you can use folloiwng code, which is not long and easy:
XmlDocument xml = new XmlDocument();
// xmlContent contains your XML file
xml.LoadXml(xmlContent);
// get collections of nodes representing translations in particular languages
var enNodes = xml.GetElementsByTagName("en");
var deNodes = xml.GetElementsByTagName("de");
string[] lines = new string[enNodes.Count];
for (int i = 0; i < enNodes.Count; i++)
lines[i] = $"{enNodes[i].InnerText},{deNodes[i].InnerText}";
File.WriteAllLines(#"path to text file", lines);
The other way, CSV to XML require much more coding as you will have to detect every node to translate and add another node representing new language. This requires much more coding and is too broad for an answer - you have to write some code yourself first, then come back with more precise question about problem you would have.
Good luck.

Child Nodes in XML File

I am trying to get the child nodes in my xml file and print it out..however i cannot. It is just printing one element instead of all. please help.
XDocument doc = XDocument.Load(GlobalClass.GlobalUrl);
XElement xelement = XElement.Load(GlobalClass.GlobalUrl);
var query = from nm in xelement.Elements("EmployeeFinance")
where (int)nm.Element("EmpPersonal_Id") == empID
select new AllowancePaid
{
Income_ID = (decimal)nm.Element("Allowance-Grade")
.Element("Amount")
};
var x = query.ToList();
foreach (var ele in x) {
Debug.WriteLine(ele.Income_ID);
}
My XML File
<EmployeeFinance>
<EmployeeEmploy_Id>4891</EmployeeEmploy_Id>
<EmpPersonal_Id>28407</EmpPersonal_Id>
<Employee_Number>11715</Employee_Number>
<Allowance-Grade>
<Amount BenListID="32">6000.00</Amount>
<Amount BenListID="59">100000.00</Amount>
</Allowance-Grade>
</EmployeeFinance>
Adding the elements
var result = from element in doc.Descendants("EmployeeFinance")
where int.Parse(element.Element("EmpPersonal_Id").Value) == tt.Employee_Personal_InfoEmp_id
select element;
foreach (var ele in result)
{
ele.Element("Allowance-Grade")
.Add(new XElement("Amount", new XAttribute("BenListID", tt.ID), tt.Amount));
doc.Save(GlobalClass.GlobalUrl);
}
problem is you selecting one amount node .Element("Amount") but there are many nodes. Change it to .Elements("Amount") and need to change few places. I haven't tested but logic should something like below will work
`
var query = from nm in xelement.Elements("EmployeeFinance")
where (int)nm.Element("EmpPersonal_Id") == empID
select new AllowancePaid
{
AllowanceList = nm.Element("Allowance-Grade").Elements("Amount").Select( a=>(decimal)a.Value).ToList()
};
foreach (var ele in query) {
foreach (var a in ele.AllowanceList) {
Debug.WriteLine(a);
}
}
XDocument xelement = XDocument.Parse(xml);
var query = from nm in xelement.Elements("EmployeeFinance")
where (int)nm.Element("EmpPersonal_Id") == 28407
select new
{
Amounts = nm.Element("Allowance-Grade")
.Elements("Amount")
.Select(
row =>
new {
id = row.Value
}
).ToList()
};
var x = query.ToList();
foreach (var ele in x)
{
foreach(var a in ele.Amounts)
Console.WriteLine(a);
}

Linq over InputStream from HttpPostedFileWrapper

Is it possible to apply a Linq query from a HttpPostedFileWrapper?
My web app allows users to select a bunch of .csv files. I now need to open those files and import them.
My previous code, which uses paths and file names looks like;
importedList = (from csvLine in File.ReadAllLines(fileName)
let x = csvLine.Split(',')
select new ImportedXDock
{
StoreNumber = int.Parse(x[0]),
DCNumber = int.Parse(x[1]),
DeliveryDay = x[2],
Activity = x[3],
ActivityDay = x[4],
Time = TimeSpan.Parse(x[5])
}).ToList();
However, now that i have a collection of HttpPostedFileWrapper objects how would I do the same?
edit
Or do I need to convert it to something and then read the file?
You may be able to loop over the file names instead of the input streams
foreach (var fileName in wrapper.Select(w => w.FileName))
{
yield return (from csvLine in File.ReadAllLines(fileName)
let x = csvLine.Split(',')
select new ImportedXDock
{
StoreNumber = int.Parse(x[0]),
DCNumber = int.Parse(x[1]),
DeliveryDay = x[2],
Activity = x[3],
ActivityDay = x[4],
Time = TimeSpan.Parse(x[5])
}).ToList();
}

LINQ XML- Read XML data

I am having trouble with LINQ query for XML and can't solve
I am trying to extract the element values
Here is the XML
<response>
<make>DODGE</make>
<make>CHRYSLER</make>
<make>JEEP</make>
<make>RAM</make>
.......etc
</response>
Here is the code I have so far...
XElement parsedXml3 = XElement.Parse(xml3);
var query2 = from m in parsedXml3.Descendants("response")
select m.Elements("make").Select(e => e.Value);
List<string> ls = new List<string>();
foreach (var o in query2)
{
ls = o.ToList();
}
ls.Sort();
Thanks for your help - extended explanations would be great
Try This.
XElement parsedXml3 = XElement.Parse(xml3); //parse your xml string
There is no need to use parsedXml3.Descendants("response") because response is root node here
var makes= parsedXml3.Elements("make") //Get All make Elements
.Select(x=>x.Value).ToList(); //Select Value from make Element
//and Store in List<string>
makes.Sort();
foreach (var make in makes)
{
Console.WriteLine(make);
}
or you can modify your code to
XElement parsedXml3 = XElement.Parse(xml3);
var ls= (from m in parsedXml3.Descendants("make")
select m.Value).ToList();
//List<string> ls = new List<string>();
//foreach (var o in query2)
//{
// ls.Add(o);
//}
ls.Sort();

C# Groupby Linq and foreach

I need a more efficient way of producing multiple files from my data group.
Im using a List<MyObject> type and my object has some public properties in which I need to group the data by.
I have heard of Linq and it sounds like something I could use. However Im not sure how to go about it.
I need to produce a text file for each STATE, so grouping all the MyObjects (people) by state, then running a foreach look on them to build the TEXT file.
void Main()
{
List<MyObject> lst = new List<MyObject>();
lst.Add(new MyObject{ name = "bill", state = "nsw", url = "microsoft.com"});
lst.Add(new MyObject{ name = "ted", state = "vic", url = "apple.com"});
lst.Add(new MyObject{ name = "jesse", state = "nsw", url = "google.com"});
lst.Add(new MyObject{ name = "james", state = "qld", url = "toshiba.com"});
string builder = "";
foreach (MyObject item in myObjects) {
builder += item.name + "\r\n";
builder += item.url + "\r\n" + "\r\n\r\n";
}
and out to the `StreamWriter` will be the filenames by state.
In total for the above data I need 3 files;
-nsw.txt
-vic.txt
-qld.txt
Something like this, perhaps?
var groups = lst.GroupBy(x => x.state);
foreach (var group in groups)
{
using (var f = new StreamWriter(group.Key + ".txt"))
{
foreach (var item in group)
{
f.WriteLine(item.name);
f.WriteLine(item.url);
}
}
}
You def. could use LINQ here.
lst.GroupBy(r=> r.state).ToList().ForEach(r=> {
//state= r.Key
//
foreach (var v in r)
{
}
});
The thing about linq. If you want to know how to do something in it. Think "how would I do this in SQL". The keywords are for the most part the same.
You can actually produce entire content with LINQ:
var entryFormat = "{1}{0}{2}{0}{0}{0}";
var groupsToPrint = lst
.GroupBy(p => p.state)
.Select(g => new
{
State = g.Key,
// produce file content on-the-fly from group entries
Content = string.Join("", g.Select(v => string.Format(entryFormat,
Environment.NewLine, v.name, v.url)))
});
var fileNameFormat = "{0}.txt";
foreach (var entry in groupsToPrint)
{
var fileName = string.Format(fileNameFormat, entry.State);
File.WriteAllText(fileName, entry.Content);
}
Something like...
string builderNsw = "";
foreach (MyObject item in lst.Where(o=>o.state == 'nsw')) {
builderNsw += item.name + "\r\n";
builderNsw += item.url + "\r\n" + "\r\n\r\n";
}
...but there are probably many ways to achieve this.
Same as Above - Iterating through groups by group, can get group name also
int itemCounter = 1;
IEnumerable<DataRow> sequence = Datatables.AsEnumerable();
var GroupedData = from d in sequence group d by d["panelName"]; // GroupedData is now of type IEnumerable<IGrouping<int, Document>>
foreach (var GroupList in GroupedData) // GroupList = "document group", of type IGrouping<int, Document>
{
bool chk = false;
foreach (var Item in GroupList)
{
if (chk == false) // means when header is not inserted
{
var groupName = "Panel Name : " + Item["panelName"].ToString();
chk = true;
}
var count = itemCounter.ToString();
var itemRef = Item["reference"].ToString();
itemCounter++;
}
}

Categories