what is the best way of reading xml file using linq and the below code you will see that, I have three different loops and I feel like its not elegant or do I have options to retrofit the below code?
public static void readXMLOutput(Stream stream)
{
XDocument xml = new XDocument();
xml = LoadFromStream(stream);
var header = from p in xml.Elements("App").Elements("Application")
select p;
foreach (var record in header)
{
string noym = record.Element("nomy").Value;
string Description = record.Element("Description").Value;
string Name = record.Element("Name").Value;
string Code = record.Element("Code").Value;
}
var appRoles = from q in xml.Elements("App").Elements("Application").Elements("AppRoles").Elements("Role")
select q;
foreach (var record1 in appRoles)
{
string Name = record1.Element("Name").Value;
string modifiedName = record1.Element("ModifiedName").Value;
}
var memeber = from r in xml.Elements("App").Elements("Application").Elements("AppRoles").Elements("Role").Elements("Members")
select r;
foreach (var record2 in memeber)
{
string ExpirationDate = record2.Element("ExpirationDate").Value;
string FullName = record2.Element("FullName").Value;
}
}
UPDATED:
foreach (var record in headers)
{
..............
string Name1 = record.Attribute("Name").Value;
string UnmodifiedName = record.Attribute("UnmodifiedName").Value;
string ExpirationDate = record.Attribute("ExpirationDate").Value;
string FullName = record.Attribute("FullName").Value;
...............
}
Is that your actual code ? All those string variables you are assigning in the foreach loops only have a scope of one iteration of the loop. They are created and destroyed each time.
This may not work precisely in your case depending on the xml structure. Play around with it. Try it using LinqPad
var applications = from p in xml.Descendants("Application")
select new { Nomy = p.Element("nomy").Value
, Description = p.Element("Description").Value
, Name = p.Element("Name").Value
, Code = p.Element("Code").Value
};
var appRoles = from r in xml.Descendants("Role")
select new { Name = r.Element("Name").Value
, ModifiedName = r.Element("ModifiedName").Value
};
This answer is a hierarchical query.
var headers =
from header in xml.Elements("App").Elements("Application")
select new XElement("Header",
new XAttribute("noym", header.Element("nomy").Value),
new XAttribute("Description", header.Element("Description").Value),
new XAttribute("Name", header.Element("Name").Value),
new XAttribute("Code", header.Element("Code").Value),
from role in header.Elements("AppRoles").Elements("Role")
select new XElement("Role",
new XAttribute("Name", role.Element("Name").Value),
new XAttribute("ModifiedName", role.Element("ModifiedName").Value),
from member in role.Elements("Members")
select new XElement("Member",
new XAttribute("ExpirationDate", member.Element("ExpirationDate").Value),
new XAttribute("FullName", member.Element("FullName").Value)
)
)
);
Related
I want to use the linq and then let the user download a csv file to their speicifc location.
I learn something from internet and learning a csvhelper.
I write something which works and download to a specific location (D:\export.csv) as below.
public ActionResult YRecordReport()
{
using (var sw = new StreamWriter(#"d:\export.csv", true, Encoding.GetEncoding("big5")))
using (var writer = new CsvWriter(sw))
{
var abc = from t in db.TRecordDBSet
join s in db.SInfoDBSet on t.SId equals s.SId
orderby s.sId, t.StartToEndDate
select new TRecord()
{
StaffId = t.SId,
Date = t.StartToEndDate,
Name = s.Cname,
CourseName = t.Tname,
Organizer = t.Organizer,
Hour = t.Hour,
Score = t.Score
};
var source = abc.ToList();
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TRecord, TRecord_Data>();
});
config.AssertConfigurationIsValid();
Mapper.Initialize(cfg => cfg.CreateMap<TRecord, TRecord_Data>());
writer.Configuration.Encoding = Encoding.GetEncoding("big5");
var mapper = config.CreateMapper();
List<TRecord_Data> records = Mapper.Map<List<TRecord>, List<TRecord_Data>>(source);
foreach (var item in records)
{
writer.WriteRecord(item);
}
}
}
Because I want to let the user to download the files to their specific location, I modified the code as below, it runs ok and the export.csv files is empty. Is that I write anything not specified? and how can i let the user download and use their filename?
[MyAuthFilter(Auth = "Admin")]
public ActionResult YearTrainingRecordReport()
{
var memoryStream = new MemoryStream();
var stream = new StreamWriter(memoryStream);
var writer = new CsvWriter(stream);
var abc = from t in db.TRecordDBSet
join s in db.SInfoDBSet on t.SId equals s.SId
orderby s.SId, t.StartToEndDate
select new TRecord()
{
Sd = t.SId,
Date = t.StartToEndDate,
Name = s.Cname,
CourseName = t.Tname,
Organizer = t.Organizer,
Hour = t.Hour,
Score = t.Score
};
var source = abc.ToList();
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TRecord, TRecord_Data>();
});
config.AssertConfigurationIsValid();
Mapper.Initialize(cfg => cfg.CreateMap<TRecord, TRecord_Data>());
writer.Configuration.Encoding = Encoding.GetEncoding("big5");
var mapper = config.CreateMapper();
List<TRecord_Data> records = Mapper.Map<List<TRecord>, List<TRecord_Data>>(source);
foreach (var item in records)
{
writer.WriteRecord(item);
}
return File(memoryStream, "text/csv", "export.csv");
}
You need to flush your writer and reset the stream.
writer.Flush();
stream.Position = 0;
If you're using CsvHelper 3.0 or later (currently in pre-release), you'll also need to force a new record.
writer.NextRecord();
how to load xml into collection and view in datagrid.
My Xml file
Add new record in xml
private void addToXml(bool value)
{
List<klientprywatny> klienci = new List<klientprywatny>();
klienci.Add(new klientprywatny() { Imie = txtImie.Text, Nazwisko = txtNazwisko.Text, miasto = txtMiasto.Text, ulica = txtUlica.Text,
kodpocztowy = txtKodPocztowy.Text, telefon = txtTelefon.Text, email = txtEmail.Text, numer = txtNumer.Text });
XDocument doc = XDocument.Load("Osoby.xml");
var osoba =
from klient in klienci
orderby klient.Nazwisko, klient.Imie
select new XElement("osoba",
new XElement("imie", klient.Imie),
new XElement("nazwisko", klient.Nazwisko),
new XElement("miasto", klient.miasto),
new XElement("ulica", klient.ulica),
new XElement("kodpocztowy", klient.kodpocztowy),
new XElement("telefon", klient.telefon),
new XElement("email", klient.email),
new XElement("numer", klient.numer)
);
doc.Root.Element("prywatni").Add(osoba);
doc.Save("Osoby.xml");
}
I tried
XDocument xml = XDocument.Load("Osoby.xml");
List<klientprywatny> klienci = (
from osoba in xml.Root.Elements("osoba")
select new osoba(
osoba.Element("imie").Value,
osoba.Element("nazwisko").Value,
osoba.Element("miasto").Value,
osoba.Element("ulica").Value,
osoba.Element("kodpocztowy").Value,
osoba.Element("telefon").Value,
osoba.Element("email").Value,
osoba.Element("numer").Value
)
).ToList<klientprywatny>();
but it does not work
You can get the elements from XML to your list as follows:
IEnumerable<osoba> result = from c in xml.Descendants("osoba")
select new osoba()
{
Imie = (string)c.Element("imie").Value,
Nazwisko = (string)c.Element("nazwisko").Value,
miasto = (string)c.Element("miasto").Value,
ulica = (string)c.Element("ulicia").Value,
kodpocztowy = (string)c.Element("kodpocztowy").Value,
email = (string)c.Element("telefon").Value,
numer = (string)c.Element("email").Value,
telefon = (string)c.Element("numer").Value,
};
I've tested this code, it works perfectly.
Hope it helps.
I have the following responses from the API. How can I group them into the following structure?
Student[]
- Name
- Classes[]
- ClassName
- ClassId
- ClassCategories[]
- CategoryName
- CategoryWeight
- Assignments[]
- AssignmentName
- Score
I was managed to group them until the "Classes" level but unable to get the ClassCategories for each of the classes
var data = (from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial }
into StudentGroup
select new GroupedStudent
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = from result in results
group result by new { result.ClassId, result.ClassName } into ClassGroup
select new groupedClass
{
ClassName = ClassGroup.Key.ClassName,
ClassId = ClassGroup.Key.ClassId,
ClassCategories = ...
})
}).ToList();
Can anyone please assists me? Thank you.
First, you have make ClassGroup from StudentGroup not from results.
Classes = from s in StudentGroup group result by new { s.ClassId, s.ClassName } into ClassGroup
The complete linq query is as follows:
var data =
(from result in results
group result by new { result.StudentId, result.FirstName, result.LastName, result.MiddleInitial } into StudentGroup
select new
{
StudentId = StudentGroup.Key.StudentId,
FullName = string.Format("{0} {1} {2}", StudentGroup.Key.FirstName, StudentGroup.Key.MiddleInitial, StudentGroup.Key.LastName).Replace(" ", " "),
Classes = (from s in StudentGroup
group s by new { s.ClassId, s.ClassName } into ClassGroup
select new
{
ClassId = ClassGroup.Key.ClassId,
ClassName = ClassGroup.Key.ClassName,
ClassCategories = (from c in ClassGroup
group c by new { c.CategoryName, c.CategoryWeight } into CategoryGroup
select new
{
CategoryName = CategoryGroup.Key.CategoryName,
CategoryWeight = CategoryGroup.Key.CategoryWeight,
Assignments = (from ct in CategoryGroup
group ct by new { ct.AssignmentName, ct.Score } into AssingnmentGroup
select new
{
AssignmentName = AssingnmentGroup.Key.AssignmentName,
Score = AssingnmentGroup.Key.Score
}).ToList()
}).ToList()
}).ToList()
}).ToList();
For example, if you want to access to the first Assignment's score, you can get it like this:
var student = data.FirstOrDefault();
var score = student.Classes[0].ClassCategories[0].Assignments[0].Score;
This is usually how I do It.
Create a class to store your data
Create a list of that class type
In your case instead of string dataRow maybe you can use a sub class
.
// get data from webservice
var json = webClient.DownloadString(url);
var values = JsonConvert.DeserializeObject<JArray>(json);
// create a list to save all the element
List<myClass> classList = new List<myClass>();
// process every row
foreach (string dataRow in values)
{
string[] dataField = dataRow.Split(',');
// have a constructor to assign each value to this element
myClass ctROW = new myClass(dataField);
classList.add(ctROW );
How would I go about getting the ID information using Linq. I'm trying to add them to an array of int.
<FactionAttributes>
<name>Player</name>
<id>0</id>
<relationModifier>1</relationModifier>
<relations>
<id0>100</id0>
<id1>50</id1>
<id2>50</id2>
<id3>50</id3>
<id4>50</id4>
<id5>50</id5>
</relations>
</FactionAttributes>
That is my XML.
Here is the code I'm using so far.
void InitFactions()
{
int count = 0;
string filepath = Application.dataPath + "/Resources/factiondata.xml";
XDocument factionXML = XDocument.Load(filepath);
var factionNames = from factionName in factionXML.Root.Elements("FactionAttributes")
select new {
factionName_XML = (string)factionName.Element("name"),
factionID_XML = (int)factionName.Element("id"),
factionRelations_XML = factionName.Element("relations")// Need to turn this into array.
};
foreach ( var factionName in factionNames)
++count;
foreach ( var factionName in factionNames)
{
Factions f = new Factions();
f.otherFactionsName = new string[count];
f.otherFactionsRelation = new int[count];
int others = 0;
f.FactionName = factionName.factionName_XML;
Debug.Log(factionName.factionRelations_XML);
// Adds Rivals, not self to other list.
foreach (var factionName2 in factionNames)
{
if (factionName.factionID_XML == factionName2.factionID_XML)
continue;
f.otherFactionsName[(int)factionName2.factionID_XML] = factionName2.factionName_XML;
// THIS IS WHERE IM ADDING THE RELATIONS IN //
f.otherFactionsRelation[(int)factionName2.factionID_XML] = factionName.factionRelations_XML[(int)factionName2.factionID_XML];
Debug.Log(f.FactionName + " adds: " + factionName2.factionName_XML);
++others;
}
}
}
I have made multiple attempts using nodes and what not. I can't seem to figure out the correct syntax.
XDocument doc = XDocument.Load(Path);
//To get <id>
var MyIds = doc.Element("FactionAttributes").Element("id").Value;
//To get <id0>, <id1>, etc.
var result = doc.Element("FactionAttributes")
.Element("relations")
.Elements()
.Where(E => E.Name.ToString().Contains("id"))
.Select(E => new { IdName = E.Name, Value = E.Value});
If you want array of ints replace the select with this
.Select(E => Convert.ToInt32(E.Value)).ToArray();
If you are just after the relations Ids use this simple query
var doc = XDocument.Load("c:\\tmp\\test.xml");
var ids = doc.Descendants("relations").Elements().Select(x => x.Value);
If you want the Id and the relations ids in one array use this
var id = doc.Descendants("id").Select(x=>x.Value).Concat(doc.Descendants("relations").Elements().Select(x => x.Value));
var instructions = (from item in config.Elements("import")
select new
{
name = item.Attribute("name").Value,
watchFolder = item.Attribute("watchFolder").Value,
root = item.Element("documentRoot").Value,
DocumentNameDynamic = item.Element("documentName").Attribute("xpath").Value,
DocumentNameStatic = item.Element("documentName").Attribute("static").Value,
TemplateName = item.Element("template").Attribute("template").Value,
Path = item.Element("path").Attribute("path").Value,
fields = item.Element("fields").Elements()
}).SingleOrDefault();
var fields = from item in instructions.fields
select new
{
xpath = item.Attribute("xpath").Value,
FieldName = item.Attribute("FieldName").Value,
isMultiValue = bool.Parse(item.Attribute("multiValue").Value)
};
I think something like this should work. I added the Select method to return the anonymous class.
var instructions = (from item in config.Elements("import")
select new
{
name = item.Attribute("name").Value,
watchFolder = item.Attribute("watchFolder").Value,
root = item.Element("documentRoot").Value,
DocumentNameDynamic = item.Element("documentName").Attribute("xpath").Value,
DocumentNameStatic = item.Element("documentName").Attribute("static").Value,
TemplateName = item.Element("template").Attribute("template").Value,
Path = item.Element("path").Attribute("path").Value,
fields = item.Element("fields").Elements().Select(item => new {
xpath = item.Attribute("xpath").Value,
FieldName = item.Attribute("FieldName").Value,
isMultiValue = bool.Parse(item.Attribute("multiValue").Value)
}
).SingleOrDefault();
If you don't want to use the Select Extension method, you can use LINQ syntax. Here is an example of this.
var instructions = (from item in config.Elements("import")
select new
{
name = item.Attribute("name").Value,
watchFolder = item.Attribute("watchFolder").Value,
root = item.Element("documentRoot").Value,
DocumentNameDynamic = item.Element("documentName").Attribute("xpath").Value,
DocumentNameStatic = item.Element("documentName").Attribute("static").Value,
TemplateName = item.Element("template").Attribute("template").Value,
Path = item.Element("path").Attribute("path").Value,
fields = from e in item.Element("fields").Elements()
select new {
xpath = item.Attribute("xpath").Value,
FieldName = item.Attribute("FieldName").Value,
isMultiValue = bool.Parse(item.Attribute("multiValue").Value)
} // End of inner select statement
} // End of outer select statement
).SingleOrDefault();