This loads a set of values from an XML file and places them into a class for storage. I'm trying to figure out how to output the values as a list so I can place them into a Listbox.
I thought there would be an easy way like a .ToList() method or to be able to foreach through the strings in the class (no public GetEnumerator). I've been able to find out that Foreach hides some of the complexity but not away to do what I want.
I've been searching online with no avail (lacking the correct terminology maybe), unfortunately I left my C# reference books at work :/
Would much appreciate a pointer in the right direction,
Thanks.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
namespace ThereIsOnlyRules
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
listBox1.Items.Clear();
string path = "characterXML.xml";
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
System.Xml.XmlDocument CXML = new System.Xml.XmlDocument();
CXML.Load(fs);
//Get the number of elements
XmlNodeList elemList = CXML.GetElementsByTagName("unit");
//foreach (var element in elemList)
//{
// listBox1.Items.Add(element);
//}
for (int i = 0; i < elemList.Count; i++)
{
UnitAttributes attributes = new UnitAttributes();
attributes.army = elemList[i].Attributes["army"].Value;
attributes.category = elemList[i].Attributes["category"].Value;
attributes.type = elemList[i].Attributes["type"].Value;
attributes.composition = elemList[i].Attributes["composition"].Value;
attributes.WS = elemList[i].Attributes["WS"].Value;
attributes.BS = elemList[i].Attributes["BS"].Value;
attributes.T = elemList[i].Attributes["T"].Value;
attributes.W = elemList[i].Attributes["W"].Value;
attributes.I = elemList[i].Attributes["I"].Value;
attributes.A = elemList[i].Attributes["A"].Value;
attributes.LD = elemList[i].Attributes["LD"].Value;
attributes.save = elemList[i].Attributes["Save"].Value;
attributes.armour = elemList[i].Attributes["armour"].Value;
attributes.weapons = elemList[i].Attributes["weapons"].Value;
attributes.specialrules = elemList[i].Attributes["specialrules"].Value;
attributes.transport = elemList[i].Attributes["transport"].Value;
attributes.options = elemList[i].Attributes["options"].Value;
//foreach (string item in attributes)
//{
//unit.Add(item);
//}
//listBox1.Items.AddRange(attributes)
}
//Close the filestream
fs.Close();
}
catch (Exception ex)
{
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ThereIsOnlyRules
{
class UnitAttributes
{
public string army { get; set; }
public string category { get; set; }
public string type { get; set; }
public string composition { get; set; }
public string WS { get; set; }
public string BS { get; set; }
public string T { get; set; }
public string W { get; set; }
public string I { get; set; }
public string A { get; set; }
public string LD { get; set; }
public string save { get; set; }
public string armour { get; set; }
public string weapons { get; set; }
public string specialrules { get; set; }
public string transport { get; set; }
public string options { get; set; }
}
}
<?xml version="1.0"?>
<config>
<unit
army="Tyranids"
category="Troops"
type="Infantry"
composition="10-30"
WS="3"
BS="3"
T="3"
W="1"
I="4"
A="1"
LD="6"
Save="6+"
armour="Chitin"
weapons="Claws and Teeth, Fleshborer"
specialrules="Instictive Behaviour - Lurk, Move Through Cover"
transport="If the brood consists of 20 models or less, it may take a Mycetic Spore."
options="Strangleweb, Spinefists, Spike rifle, Devourer, Adrenal Glands, Toxin Sacs"
>
Termagant Brood
</unit>
<unit
army="Tyranids"
category="Troops"
type="Infantry"
composition="10-30"
WS="3"
BS="3"
T="3"
W="1"
I="5"
A="2"
LD="6"
Save="6+"
armour="Chitin"
weapons="Scything Talons"
specialrules="Instictive Behaviour - Feed, Bounding Leap, Fleet, Move Through Cover"
transport="If the brood consists of 20 models or less, it may take a Mycetic Spore."
options="Adrenal Glands, Toxin Sacs"
>
Hormagaunt Brood
</unit>
</config>
Are the members of your class fields or properties? Either way, a little reflection and Linq should allow you to enumerate through the data members of your class, after you have hydrated an instance of it from your XML file.
var fieldDictionary =
(from f in typeof(UnitAttributes).GetFields()
select new {Name = f.Name, Value = (string)(f.GetValue(attributes))})
.ToDictionary(x=>x.Name, x=>x.Value);
fieldDictionary is now a Dictionary<string, string> (which is an IEnumerable<KeyValuePair<string, string>>), which should be suitable for loading into a ListBox.
Be advised; reflection is slow. It would be far more preferable for you to modify or extend your UnitAttributes class to implement IEnumerable (probably of a Tuple, maybe a KeyValuePair). It would also allow you to enumerate the properties of an instance of the class in exactly the order you want, instead of the order in which they're defined, or by some other FieldInfo/PropertyInfo data like the field's name.
Also be aware that a field is not a property, and vice versa. If you have a mix of properties and public fields on your class, I would HIGHLY recommend standardizing to one or the other; otherwise you'll have to reflect BOTH a list of properties and a list of fields using two of the above Linq statements (at a cost of roughly double the run time), and there will be no chance of them being in any custom-defined order.
You'll save yourself a lot of time and effort if you use a common serializer like XmlSerializer to handle converting your objects to/from strings. You don't have to write this type of code from scratch.
Related
I am having quite some trouble passing the Class type as paramater, since they represent tables and i only want to retrieve the result as a list
My function that takes the list is setDados(ds)
public void setDados(List<RP_EntityFrameWork_Tentativa.marcas> data)
{
gridControl1.DataSource = data;
gridView1.PopulateColumns();
Debug.WriteLine(data);
}
Which i have to before hand specify what class the list type is.
is there a way to be able to declare something along the lines of List<Class> or just pass a class as a parameter ?
all the classes i need to pass follow this same "Template"/Writing Style
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RP_EntityFrameWork_Tentativa
{
public class marcas
{
[Key]
[Required]
[StringLength(50)]
public string Marca { get; set; }
[StringLength(50)]
public string Descmarca { get; set; }
[StringLength(50)]
public string Nat { get; set; }
//ligação carros
public virtual ICollection<carros> carros{ get; set; }
}
}
i am currently using the following method
private void abreListaGeral(DevExpress.XtraEditors.ButtonEdit txt, List<marcas> ds, bool multiselect, int mode, int indexProcura)
{
this.Cursor = Cursors.WaitCursor;
String str = "";
frmQueryList lista = new frmQueryList();
lista.multiselect = multiselect;
lista.mode = mode;
lista.indexProcura = indexProcura;
lista.btnNovo.Visibility = DevExpress.XtraBars.BarItemVisibility.Never;
lista.btnNovo.Enabled = false;
lista.setDados(ds);
lista.ShowDialog();
str = lista.resultado2;
txt.Text = lista.resultado;
lista.frmIniciado = false;
lista.Close();
this.Cursor = Cursors.Default;
}
Which works but i have to create a new method for every Class i need to use this way create a bunch of overrites and useless code that could surely be optimized
What i am looking for is to have abreListaGeral() take as arguments such as List<marcas> List<carros> List<etc> without having to previously specify it
I'm working on a project that requires that I take data from an XML API that I don't control and insert that data into a database (I control) for consumption by a different application (I control but can't modify code too it)
We already managed to get the full database layout from the XML API via the providers special endpoint but all other queries (to get data) are via XML.
Currently what I have is the following:
Book.cs - This represents one of the database tables
using System;
using System.ComponentModel.DataAnnotations;
namespace Example.Models
{
public partial class Book
{
[Key]
public int Recordno { get; set; }
public decimal? Amount { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? Whenmodified { get; set; }
public DateTime? Whencreated { get; set; }
public int? Createdby { get; set; }
public int? Modifiedby { get; set; }
}
}
API Return XML - This represents the XML returned by the API (an SDK already converts it to an XMLDocument Type)
<BOOKS>
<BOOK>
<RECORDNO>1</RECORDNO>
<AMOUNT>24.12</AMOUNT>
<TITLE>This is a title</TITLE>
<DESCRIPTION>This is a description</DESCRIPTION>
<WHENMODIFIED></WHENMODIFIED>
<WHENCREATED>13/03/20 15:23:12</WHENCREATED>
<CREATEDBY>3</CREATEDBY>
<MODIFIEDBY></MODIFIEDBY>
</BOOK>
<BOOK>
<RECORDNO>1</RECORDNO>
<AMOUNT>24.12</AMOUNT>
<TITLE>This is another title</TITLE>
<DESCRIPTION>This is another description</DESCRIPTION>
<WHENMODIFIED>21/03/20 12:45:23</WHENMODIFIED>
<WHENCREATED>15/03/20 15:23:12</WHENCREATED>
<CREATEDBY>6</CREATEDBY>
<MODIFIEDBY>2</MODIFIEDBY>
</BOOK>
</BOOKS>
Currently the way we are doing this is via switch statements, but I'm hoping that there is a better method.
Partial BookController.cs
var books = new List<Book>();
// response.Data return is a List<XElement>
foreach (var result in response.Data)
{
var xElements = result.Elements();
var book = new Book();
foreach (var element in xElements)
{
switch (element.Name.ToString())
{
case "RECORDNO":
book.Recordno = int.Parse(element.Value);
break;
case "AMOUNT":
if (element.Value != string.Empty)
{
book.Amount = int.Parse(element.Value);
}
break;
// etc.
}
}
books.Add(book);
}
I have a feeling that there is a much easier method of doing this that I'm just unaware of, my concern is that I have about fifty tables and hundreds of elements to do making this method inefficient time wise.
Use xml serialization
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication8
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Books));
Books books = (Books)serializer.Deserialize(reader);
}
}
[XmlRoot("BOOKS")]
public class Books
{
[XmlElement("BOOK")]
public List<Book> books { get; set; }
}
public partial class Book
{
//[Key]
[XmlElement("RECORDNO")]
public int Recordno { get; set; }
[XmlElement("AMOUNT")]
public decimal? Amount { get; set; }
[XmlElement("TITLE")]
public string Title { get; set; }
[XmlElement("DESCRIPTION")]
public string Description { get; set; }
private DateTime Whenmodified { get;set;}
[XmlElement("WHENMODIFIED")]
public string _Whenmodified {
get { return Whenmodified.ToString("dd/MM/yy HH:mm:ss"); }
set { DateTime.ParseExact(value,"dd/MM/yy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);}
}
private DateTime Whencreated { get; set; }
public string _Whencreated
{
get { return Whencreated.ToString("dd/MM/yy HH:mm:ss"); }
set { DateTime.ParseExact(value, "dd/MM/yy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture); }
}
[XmlElement("CREATEDBY")]
public int? Createdby { get; set; }
[XmlElement("MODIFIEDBY")]
public int? Modifiedby { get; set; }
}
}
You could do the following:
use XML Schema Definition Tool (Xsd.exe) to generate classes
use generated classes to read the input XML
use that data
Welcome to SO 👋
What you're trying to do here of construct an in-memory C# object from an XML string representation is known as deserialization. There are lots of resources on this (e.g. MSDN), but here's a link to a SO post where the OP was looking to achieve something similar.
Applied to your above code snippet might look something like this:
var books = new List<Book>();
var serializer = new XmlSerializer(typeof(Book));
// response.Data return is a List<XElement>
foreach (var result in response.Data)
{
var book = (Book)serializer.Deserialize(result.CreateReader());
books.Add(book);
}
Sorry for the somewhat basic question, but what can I say. I can't figure it out. The problem is that there's a foreach loop that's supposed to iterate through the rows (sections) and while it works for the first section, the second time through the loop it doesn't seem to read the second section. The same data is stored in version. BTW, the way the method is called I would be passing in ProductName as a parameter (There will be multiple products represented here and also a version number (e.g. v2.0.0) that I'll need to filter the results for too.
So I have an XML file that looks like this:
<Products>
<ProductName1>
<v2.0.0>
<GUID>"{B5ECEC43-5406-4E4D-96D9-456823100313}"</GUID>
<VersionNameToUninstall>"2.0.0 - 2.0.2"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.0.0-Uninst.iss"</UninstallResponseFile>
</v2.0.0>
<v2.0.3>
<GUID>"{1D6C02D7-8E87-43BE-8AB2-1FF0E5ACD410}"</GUID>
<VersionNameToUninstall>"2.0.3"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.0.3-Uninst.iss"</UninstallResponseFile>
</v2.0.3>
</ProductName1>
<ProductName2>
<v3.0.0>
<GUID>"{ABCDEC43-5406-4E4D-96D9-456823101234}"</GUID>
<VersionNameToUninstall>"2.2.0 - 2.2.2"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-2.2.0-Uninst.iss"</UninstallResponseFile>
</v3.0.0>
<v4.0.0>
<GUID>"{5D6C02D7-8E87-43BE-8AB2-1FF0E5ACD589}"</GUID>
<VersionNameToUninstall>"4.0.0"</VersionNameToUninstall>
<UninstallResponseFile>"GVQC-Client-4.0.0-Uninst.iss"</UninstallResponseFile>
</v4.0.0>
</ProductName2>
</Products>
There will only be 10 or so versions (e.g. v2.x.x) so there's not a lot of data here. So I created a multidimensional (nested) class/struct to hold the data and when I try my code to read the data it's not working.
Here are the classes/stucts (I've tried both and neither works) that I'm trying to populate:
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row {get;set;}
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
So here's my code. Please just ignore the Stream - that's so I can embed this XML file in the .exe and not have it be a separate file:
public static List<TopLevelObject> GetGUIDSFromFile(string GUIDKey)
List<InstallScriptMSIXMLTopLevelObject> installScriptMSIXMLTopLevelObjectList = new List<InstallScriptMSIXMLTopLevelObject>();
Stream GUIDXmlFileStream = typeof(PGCommonCA).Assembly.GetManifestResourceStream("PGCommonCA.ProductGUIDs.xml");
XElement xElement = XElement.Load(GUIDXmlFileStream);
var versions = xElement.Elements(GUIDKey).Descendants();
foreach (var version in versions)
{
TopLevelObject topLevelObject = new TopLevelObject();
RowLevelObject rowLevelObject = new RowLevelObject();
TopLevelObject.Version = version.Name.LocalName;
RowLevelObject.Guid = version.Element("GUID").Value;
RowLevelObject.VersionName = version.Element("VersionNameToUninstall").Value;
RowLevelObject.UninstallFileName = version.Element("UninstallResponseFile").Value;
TopLevelObjectList.Add(topLevelObject);
}
return TopLevelObjectList;
}
I know there are many ways to read XML and my choice doesn't work so I'm looking for another simple solution.
The following works :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement productName = doc.Root;
List<TopLevelObject> top = productName.Elements().Select(x => new TopLevelObject() {
Version = x.Name.LocalName,
Row = new RowLevelObject() {
Guid = (string)x.Element("GUID"),
VersionName = (string)x.Element("VersionNameToUninstall"),
UninstallFileName = (string)x.Element("UninstallResponseFile")
}
}).ToList();
}
}
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row { get; set; }
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
}
I figured it out (many thanks to jdweng!!). Here's the final solution based on the revised XML at the top:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static TopLevelObject GetInfo(string xmlKey)
{
XDocument doc = XDocument.Load(FILENAME);
XElement productName = doc.Root;
List<TopLevelObject> top = productName.Descendants(xmlKey).Elements().Select(x => new TopLevelObject() {
Version = x.Name.LocalName,
Row = new RowLevelObject() {
Guid = (string)x.Element("GUID"),
VersionName = (string)x.Element("VersionNameToUninstall"),
UninstallFileName = (string)x.Element("UninstallResponseFile")
}
}).ToList();
}
}
public class TopLevelObject
{
public string Version { get; set; }
public RowLevelObject Row { get; set; }
}
public struct RowLevelObject
{
public string Guid { get; set; }
public string VersionName { get; set; }
public string UninstallFileName { get; set; }
}
}
I'm creating my first app in Xamarin.forms and want to add information about the characters. I followed the Microsoft docs but I keep getting the error that ".Add does not exist in the current context"
I've been the last hour or two searching online but nothing seems to have fixed it. Any help would be greatly appreciated, thanks!
using SQLite;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace CharCreator
{
public class Character
{
[PrimaryKey, AutoIncrement]
public int charIndex { get; set; }
public string charName { get; set; }
public string charClass { get; set; }
public string charRace { get; set; }
public int[] charStats { get; set; }
public int classId { get; set; }
public string className { get; set; }
}
}
public class CharClasses
{
List<CharClasses> classList = new List<CharClasses>();
classList.Add(new CharClasses() {classId = 1, className = "Barbarian"});
}
Your problem begins with the declaration
List<CharClasses> classList = new List<CharClasses>();
You are declaring a List of CharClasses instead of a List of Character. Then you try initialize this list with a first element. But you cannot add code outside a method.
So, if you really need to have CharClasses initialized with a List<Character> containig at least one element then you need to write this
public class CharClasses
{
public List<Character> classList = new List<Character>()
{
new Character {classId = 1, className = "Barbarian"}
};
--- other class method follows
}
This syntax is explained in documentation at Object and Collection Initializers
Is there any way to retrieve a table from a web service to a wp7 app page as its?
i just wanna sth easy to use than making a table each time getting data
Have look on MSDN for System.Xml namespace. It contains lot of usable classes. You should use XmlReader or something similair to load it to array or generic collection. Hope this helps, 'cause bit unclear question.
EDIT:
This is the code what have I done for loading sample data to generic collection:
Sample data:
<?xml version="1.0" encoding="utf-8" ?>
<studentPunishmentsTables>
<studentPunishmentsTable>
<fromSemester/>
<fromSemesterDesc/>
<issueDate>01/04/2012</issueDate>
<note/>
<penalty>Course Failure</penalty>
<semester>311</semester>
<semesterDesc>First Semester 31/32</semesterDesc>
<toSemester/>
<toSemesterDesc/>
</studentPunishmentsTable>
<studentPunishmentsTable>
<fromSemester/>
<fromSemesterDesc/>
<issueDate>01/04/2012</issueDate>
<note/>
<penalty>Semester Failure</penalty>
<semester>311</semester>
<semesterDesc>First Semester 31/32</semesterDesc>
<toSemester/>
<toSemesterDesc/>
</studentPunishmentsTable>
</studentPunishmentsTables>
Code:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
namespace XMLStudent
{
class Program
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.Load("st.xml");
List<StudentPunishment> sp = new List<StudentPunishment>();
foreach (XmlNode nod in doc.SelectNodes(#"studentPunishmentsTables/studentPunishmentsTable"))
{
StudentPunishment s = new StudentPunishment();
s.FromSemester = nod.ChildNodes[0].InnerText;
s.FromSemesterDesc = nod.ChildNodes[1].InnerText;
s.IssueDate = nod.ChildNodes[2].InnerText;
s.Note = nod.ChildNodes[3].InnerText;
s.Penalty = nod.ChildNodes[4].InnerText;
s.Semester = nod.ChildNodes[5].InnerText;
s.SemesterDesc = nod.ChildNodes[6].InnerText;
s.ToSemester = nod.ChildNodes[7].InnerText;
s.ToSemesterDesc = nod.ChildNodes[8].InnerText;
sp.Add(s);
}
Console.WriteLine(sp[0].IssueDate);
Console.Read();
}
}
class StudentPunishment
{
public string FromSemester { get; set; }
public string FromSemesterDesc { get; set; }
public string IssueDate { get; set; }
public string Note { get; set; }
public string Penalty { get; set; }
public string Semester { get; set; }
public string SemesterDesc { get; set; }
public string ToSemester { get; set; }
public string ToSemesterDesc { get; set; }
}
}
So. This code is loading XMLDocument with sample data and it's selecting data of each studentPunishmentsTable into new object of StudentPunishment class. There're properties for holding that data. After everything is done and object of student's punishment is added into generic collection ('List'), code is trying to show date of first object in collection. You can test is yourself, it's working for me.