Is there smart way to parse xml to list? - c#

There is xml file, which have next structure:
<?xml version="1.0"?>
<myobjectlist objectCount = "3">
<myobject Number = "0" Name="My First Object" MyChildObectsCount = "2">
<intProp>5</intProp>
<stringProp>Str1</stringProp>
<doubleProp>35.1</doubleProp>
<MyChildObj Number = "100" Name = "My first child object">
<childIntProp>1</childIntProp>
<childStringProp>CStr1</childStringProp>
</MyChildObj>
<MyChildObj Number = "120" Name = "My child object">
<childIntProp>15</childIntProp>
<childStringProp>CStr2</childStringProp>
</MyChildObj>
</myobject>
<myobject Number = "145" Name="My second Object" MyChildObectsCount = "1">
<intProp>96</intProp>
<stringProp>Str2</stringProp>
<doubleProp>+Inf</doubleProp>
<MyChildObj Number = "250" Name = "This's child object">
<childIntProp>62</childIntProp>
<childStringProp>CStr3</childStringProp>
</MyChildObj>
</myobject>
<myobject Number = "261" Name="My last Object" MyChildObectsCount = "3">
<intProp>9</intProp>
<stringProp>Str45</stringProp>
<doubleProp>1.6449635e+07</doubleProp>
<MyChildObj Number = "150" Name = "Almost last child object">
<childIntProp>-1</childIntProp>
<childStringProp>CStr41</childStringProp>
</MyChildObj>
<MyChildObj Number = "680" Name = "Prelast child object">
<childIntProp>72</childIntProp>
<childStringProp>CStr42</childStringProp>
</MyChildObj>
<MyChildObj Number = "127" Name = "Last child object">
<childIntProp>64</childIntProp>
<childStringProp>CStr222</childStringProp>
</MyChildObj>
</myobject>
</myobjectlist>
I've tried to use XMLSerializer, but how I found out it can't deserialize to list, all objects of the list will be the first object of deserializable xml, so me result list will consist ObjCount of 0th obj.
I've created classes -- enteties of xml objects, let's say
public class MyObject{
int Number;
string Name;
int IntProp;
string stringProp;
double doubleProp;
List<MyChildObject> myChildObjects;
}
public class MyChildObject{
int Number;
string Name;
int childIntProp;
string childStringProp;
}
I need to get List<MyObject> from XML file, but I do not want to parse it node by node. Is there smart way to do it?
UPDATE
And here, what I got: Empty list

If you annotate your data model like this:
[XmlRoot(ElementName = "myobjectlist")]
public class MyObjectList
{
[XmlAttribute(AttributeName = "objectCount")]
public string ObjectCount { get; set; }
[XmlElement(ElementName = "myobject")]
public List<MyObject> MyObjects { get; set; }
}
[XmlRoot(ElementName = "myobject")]
public class MyObject
{
[XmlAttribute]
public string Number { get; set; }
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute(AttributeName = "MyChildObectsCount")]
public string MyChildObjectsCount { get; set; }
[XmlElement(ElementName = "intProp")]
public string IntProp { get; set; }
[XmlElement(ElementName = "stringProp")]
public string StringProp { get; set; }
[XmlElement(ElementName = "doubleProp")]
public string DoubleProp { get; set; }
[XmlElement(ElementName = "MyChildObj")]
public List<MyChildObject> MyChildObjects { get; set; }
}
[XmlRoot(ElementName = "MyChildObj")]
public class MyChildObject
{
[XmlAttribute]
public string Number { get; set; }
[XmlAttribute]
public string Name { get; set; }
[XmlElement(ElementName = "childIntProp")]
public string ChildIntProp { get; set; }
[XmlElement(ElementName = "childStringProp")]
public string ChildStringProp { get; set; }
}
The the deseralization logic is this simple:
StreamReader reader = new StreamReader(File.OpenRead("sample.xml"));
var serializer = new XmlSerializer(typeof(MyObjectList));
var data = (MyObjectList)serializer.Deserialize(reader);
UPDATE #1
UPDATE #2
If you need to handle
+Inf as float.PositiveInfinity
-Inf as float.NegativeInfinity
then you need to change the text of the <doubleProp>. One way to do this is the following:
var malformedXml = File.ReadAllText("sample.xml");
var fixedXml = malformedXml
.Replace("<doubleProp>+Inf</doubleProp>", "<doubleProp>Infinity</doubleProp>")
.Replace("<doubleProp>-Inf</doubleProp>", "<doubleProp>-Infinity</doubleProp>");
var fixedXmlStream = new MemoryStream();
var writer = new StreamWriter(fixedXmlStream);
writer.Write(fixedXml);
writer.Flush();
fixedXmlStream.Position = 0;
var reader = new StreamReader(fixedXmlStream);

Related

How to convert json file to List<MyClass>

I am trying to create a log file in json format from a List.
my class for list is
public class ChunkItem
{
public int start { get; set; }
public int end { get; set; }
}
public class DownloadItem
{
public int id { get; set; }
public string fname { get; set; }
public string downloadPath { get; set; }
public int chunkCount { get; set; }
public ChunkItem[] chunks { get; set; }
public DownloadItem(int _id, string _fname, string _downloadPath, int _chunkCount, ChunkItem[] _chunks)
{
id = _id;
fname = _fname;
downloadPath = _downloadPath;
chunkCount = _chunkCount;
chunks = _chunks;
}
}
creating a json file from this class works fine
ChunkItem[] chunks = new ChunkItem[2];
chunks[0] = new ChunkItem();
chunks[0].start = 0;
chunks[0].end = 0;
chunks[1] = new ChunkItem();
chunks[1].start = 0;
chunks[1].end = 0;
List<DownloadItem> lst = new List<DownloadItem>();
lst.Add(new DownloadItem(0, "", "", 2, chunks));
lst.Add(new DownloadItem(1, "aaa", "sss", 2, chunks));
lst.Add(new DownloadItem(2, "bbb", "ddd", 2, chunks));
string json = JsonConvert.SerializeObject(lst);
System.IO.File.WriteAllText(logPath, json);
I want to read the file to same class list and do some updates or add new lines
I can read the file to a string but cannot create a new list
how can I convert string (read json file) to List<DownloadItem> new list
You need to read all the contends from the file and deserialize the json string to List<DownloadItem>
var jsonData = File.ReadAllText(filePath)
var list = JsonConvert.DeserializeObject<List<DownloadItem>>(jsonData);
Clas DownloadItem is missing a default parameterless constructor.
I use Newtonsoft, where creating the instances and filling them is simple
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass>(jsonString);

Create a DataGridView out of a Xml File while using certain Elements as ColumnHeader

I currently have a XmlFile which look like the following (Note: the structure of this xmlFile is final) :
<Language>
<FileInfo NumberOfEntries="10" FileCreationTime="2017-07-14 12:23:07" />
<Entry Key="ABC_DEF_GHI" CreationTime="01.01.0001 00:00:00" LastModifiedTime="01.01.0001 00:00:00">
<LanguageEntry>
<ID>1</ID>
<Value>Hallo</Value>
<Comment>
</Comment>
<Mark>
</Mark>
</LanguageEntry>
<LanguageEntry>
<ID>2</ID>
<Value>Hello</Value>
<Comment>
</Comment>
<Mark>
</Mark>
</LanguageEntry>
...
</Entry>
...
</Language>
For now the only thing that matters are the Keys, which are given to one Entry, the ID and the Value.
I want to create a DataGridView which looks like the following:
Key | 1 | 2 | ID3 |...
---------------------------------------------
ABC_DEF_GHI|Hallo |Hello |someValue|...
---------------------------------------------
XYZ_DAF_ABC|someValue|someValue|someValue|...
. . . .
. . . .
. . . .
How can I achieve it, to make my DataGridView look like the one i showed above?
I tried using standard DataSet implementations, but I just can not think of a way of doing it. Please leave a comment, if you need any further information on what i try to get to or if something is unclear.
Thanks in advance!
First, you need to deserialize your data. In order to achive this you should have these classes.
[XmlRoot(ElementName = "FileInfo")]
public class FileInfo
{
[XmlAttribute(AttributeName = "NumberOfEntries")]
public string NumberOfEntries { get; set; }
[XmlAttribute(AttributeName = "FileCreationTime")]
public string FileCreationTime { get; set; }
}
[XmlRoot(ElementName = "LanguageEntry")]
public class LanguageEntry
{
[XmlElement(ElementName = "ID")]
public string ID { get; set; }
[XmlElement(ElementName = "Value")]
public string Value { get; set; }
[XmlElement(ElementName = "Comment")]
public string Comment { get; set; }
[XmlElement(ElementName = "Mark")]
public string Mark { get; set; }
}
[XmlRoot(ElementName = "Entry")]
public class Entry
{
[XmlElement(ElementName = "LanguageEntry")]
public List<LanguageEntry> LanguageEntry { get; set; }
[XmlAttribute(AttributeName = "Key")]
public string Key { get; set; }
[XmlAttribute(AttributeName = "CreationTime")]
public string CreationTime { get; set; }
[XmlAttribute(AttributeName = "LastModifiedTime")]
public string LastModifiedTime { get; set; }
}
[XmlRoot(ElementName = "Language")]
public class Language
{
[XmlElement(ElementName = "FileInfo")]
public FileInfo FileInfo { get; set; }
[XmlElement(ElementName = "Entry")]
public Entry Entry { get; set; }
}
And then you have to generate dynamic columns according to your data:
private void GenerateColumnsAndData(Language language)
{
var countIds = language.Entry.LanguageEntry.Count;
dataGridView1.ColumnCount = countIds + 1;
dataGridView1.Columns[0].Name = "Key";
for (int i = 0; i < language.Entry.LanguageEntry.Count; i++)
{
dataGridView1.Columns[i+1].Name = language.Entry.LanguageEntry[i].ID;
}
ArrayList row = new ArrayList();
row.Add(language.Entry.Key);
foreach (var item in language.Entry.LanguageEntry)
{
row.Add(item.Value);
}
dataGridView1.Rows.Add(row.ToArray());
}
Finally here's an example:
private void Form1_Load(object sender, EventArgs e)
{
string xml = #"<Language>
<FileInfo NumberOfEntries=""10"" FileCreationTime=""2017-07-14 12:23:07"" />
<Entry Key=""ABC_DEF_GHI"" CreationTime=""01.01.0001 00:00:00"" LastModifiedTime=""01.01.0001 00:00:00"">
<LanguageEntry>
<ID>1</ID>
<Value>Hallo</Value>
<Comment>
</Comment>
<Mark>
</Mark>
</LanguageEntry>
<LanguageEntry>
<ID>2</ID>
<Value>Hello</Value>
<Comment>
</Comment>
<Mark>
</Mark>
</LanguageEntry>
</Entry>
</Language>";
XmlSerializer serializer = new XmlSerializer(typeof(Language));
using(TextReader reader = new StringReader(xml))
{
Language result = (Language)serializer.Deserialize(reader);
GenerateColumnsAndData(result);
}
}
or if you are reading from a file try this:
using (FileStream fileStream = new FileStream("MyFilePath", FileMode.Open))
{
Language result = (Language)serializer.Deserialize(fileStream);
GenerateColumnsAndData(result);
}

Problems deserializing List of objects

I am having trouble deserializing a list of objects. I can get just one object to serialize into an object but cannot get the list. I get no error it just returns an empty List. This is the XML that gets returned:
<locations>
<location locationtype="building" locationtypeid="1">
<id>1</id>
<name>Building Name</name>
<description>Description of Building</description>
</location>
</locations>
This is the class I have and I am deserializing in the GetAll method:
[Serializable()]
[XmlRoot("location")]
public class Building
{
private string method;
[XmlElement("id")]
public int LocationID { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("mubuildingid")]
public string MUBuildingID { get; set; }
public List<Building> GetAll()
{
var listBuildings = new List<Building>();
var building = new Building();
var request = WebRequest.Create(method) as HttpWebRequest;
var response = request.GetResponse() as HttpWebResponse;
var streamReader = new StreamReader(response.GetResponseStream());
TextReader reader = streamReader;
var serializer = new XmlSerializer(typeof(List<Building>),
new XmlRootAttribute() { ElementName = "locations" });
listBuildings = (List<Building>)serializer.Deserialize(reader);
return listBuildings;
}
}
Try this:
[XmlRoot("locations")]
public class BuildingList
{
public BuildingList() {Items = new List<Building>();}
[XmlElement("location")]
public List<Building> Items {get;set;}
}
Then deserialize the whole BuildingList object.
var xmlSerializer = new XmlSerializer(typeof(BuildingList));
var list = (BuildingList)xmlSerializer.Deserialize(xml);
I know this is an old(er) question, but I struggled with this today, and found an answer that doesn't require encapsulation.
Assumption 1: You have control over the source Xml and how it is constructed.
Assumption 2: You are trying to serialise the Xml directly into a List<T> object
You must name the Root element in the Xml as ArrayOfxxx where xxx is the name of your class (or the name specified in XmlType (see 2.))
If you wish your xml Elements to have a different name to the class, you should use XmlType on the class.
NB: If your Type name (or class name) starts with a lowercase letter, you should convert the first character to uppercase.
Example 1 - Without XmlType
class Program
{
static void Main(string[] args)
{
//String containing the xml array of items.
string xml =
#"<ArrayOfItem>
<Item>
<Name>John Doe</Name>
</Item>
<Item>
<Name>Martha Stewart</Name>
</Item>
</ArrayOfItem>";
List<Item> items = null;
using (var mem = new MemoryStream(Encoding.Default.GetBytes(xml)))
using (var stream = new StreamReader(mem))
{
var ser = new XmlSerializer(typeof(List<Item>)); //Deserialising to List<Item>
items = (List<Item>)ser.Deserialize(stream);
}
if (items != null)
{
items.ForEach(I => Console.WriteLine(I.Name));
}
else
Console.WriteLine("No Items Deserialised");
}
}
public class Item
{
public string Name { get; set; }
}
Example 2 - With XmlType
class Program
{
static void Main(string[] args)
{
//String containing the xml array of items.
//Note the Array Name, and the Title case on stq.
string xml =
#"<ArrayOfStq>
<stq>
<Name>John Doe</Name>
</stq>
<stq>
<Name>Martha Stewart</Name>
</stq>
</ArrayOfStq>";
List<Item> items = null;
using (var mem = new MemoryStream(Encoding.Default.GetBytes(xml)))
using (var stream = new StreamReader(mem))
{
var ser = new XmlSerializer(typeof(List<Item>)); //Deserialising to List<Item>
items = (List<Item>)ser.Deserialize(stream);
}
if (items != null)
{
items.ForEach(I => Console.WriteLine(I.Name));
}
else
Console.WriteLine("No Items Deserialised");
}
}
[XmlType("stq")]
public class Item
{
public string Name { get; set; }
}
Not sure how Building corresponds with the location you have in your xml, but to me it makes more sense if they're named equivalently. Instead of using a List use a LocationList, and it becomes:
[Serializable()]
[XmlRoot("locations")]
public class LocationCollection{
[XmlElement("location")]
public Location[] Locations {get;set;}
}
[Serializable()]
[XmlRoot("location")]
public class Location
{
[XmlElement("id")]
public int LocationID { get; set; }
[XmlAttribute("locationtype")]
public string LocationType {get;set;}
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("mubuildingid")]
public string MUBuildingID { get; set; }
}
You can then deserialize as follows:
var request = WebRequest.Create(method) as HttpWebRequest;
var response = request.GetResponse() as HttpWebResponse;
var streamReader = new StreamReader(response.GetResponseStream());
TextReader reader = streamReader;
var serializer = new XmlSerializer(typeof(LocationCollection),
new XmlRootAttribute() { ElementName = "locations" });
var listBuildings = (LocationCollection)serializer.Deserialize(reader);
return listBuildings;
I know, old question, but came across it when faced by a similar issue.
Building on #ricovox's answer and in context of the OP's question, this is the model I would use to serialize his xml:
[Serializable, XmlRoot("locations")]
public class BuildingList
{
[XmlArrayItem("location", typeof(Building))]
public List<Building> locations { get; set; }
}
[Serializable]
public class Building
{
public int LocationID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string MUBuildingID { get; set; }
public List<Building> GetAll()
{
...
}
}
The OP's mistake was to create the list item as the root
Use [XMLArray] for collection properties.

parse xml children nodes

What is the best way to parse XML children nodes into a specific list? This is a small example of the XML.
<Area Name="Grey Bathroom" IntegrationID="3" OccupancyGroupAssignedToID="141">
<Outputs>
<Output Name="Light/Exhaust Fan" IntegrationID="46" OutputType="NON_DIM" Wattage="0" />
</Outputs>
</Area>
I want to create a list or something that will be called the Area Name and hold the information of the Output Name and IntegrationID. So I can call the list and pull out the Output Name and IntegrationID.
I can create a list of all Area Names and then a list of Outputs but cannot figure out how to create a list that will be called "Grey Bathroom" and hold the output "Light/Exhaust Fan" with an ID of 46.
XDocument doc = XDocument.Load(#"E:\a\b.xml");
List<Area> result = new List<Area>();
foreach (var item in doc.Elements("Area"))
{
var tmp = new Area();
tmp.Name = item.Attribute("Name").Value;
tmp.IntegrationID = int.Parse(item.Attribute("IntegrationID").Value);
tmp.OccupancyGroupAssignedToID = int.Parse(item.Attribute("OccupancyGroupAssignedToID").Value);
foreach (var bitem in item.Elements("Outputs"))
{
foreach (var citem in bitem.Elements("Output"))
{
tmp.Outputs.Add(new Output
{
IntegrationID = int.Parse(citem.Attribute("IntegrationID").Value),
Name = citem.Attribute("Name").Value,
OutputType = citem.Attribute("OutputType").Value,
Wattage = int.Parse(citem.Attribute("Wattage").Value)
});
}
}
result.Add(tmp);
}
public class Area
{
public String Name { get; set; }
public int IntegrationID { get; set; }
public int OccupancyGroupAssignedToID { get; set; }
public List<Output> Outputs = new List<Output>();
}
public class Output
{
public String Name { get; set; }
public int IntegrationID { get; set; }
public String OutputType { get; set; }
public int Wattage { get; set; }
}
The example uses an anonymous type. You could (and I warmly advice you to) use your own.
var doc = XDocument.Parse(xml);
var areaLists = doc.Elements("Area").
Select(e => e.Descendants("Output").
Select(d => new
{
Name = (string) d.Attribute("Name"),
Id = (int) d.Attribute("IntegrationID")
}).
ToArray()).
ToList();

Convert xml to List by Deserialize in c#

i have a xml file and i am trying to populate my list with the data through deserialization.
my xml is here
<?xml version="1.0" ?>
<CustomerQueryRs>
<CustomerRet>
<ListID>6BE0000-1159990808</ListID>
<Name>+ Blaine Bailey</Name>
<FullName>+ Blaine Bailey</FullName>
<Phone>866-855-0800</Phone>
</CustomerRet>
<CustomerRet>
<ListID>9BA0000-1165353294</ListID>
<Name>+ Brian Boyd</Name>
<FullName>+ Brian Boyd</FullName>
<Phone>203-245-1877</Phone>
</CustomerRet>
<CustomerRet>
<ListID>9280000-1164147562</ListID>
<Name>+ Brian Leahy</Name>
<FullName>+ Brian Leahy</FullName>
<Phone>508-341-0955</Phone>
</CustomerRet>
</CustomerQueryRs>
here i am giving my full code. i just do not understand why my code is not working...what is missing in my code......it is not giving error but list is not getting populated. so please tell me which area i need to rectify in code.
[XmlTypeAttribute(AnonymousType = true)]
public class CustomersData
{
[XmlArray(ElementName = "CustomerQueryRs")]
[XmlArrayItem(ElementName = "CustomerRet")]
public List<Customer> Customers { get; set; }
public CustomersData()
{
Customers = new List<Customer>();
}
}
public class Customer
{
[XmlElement(ElementName = "ListID")]
public string ListID { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "FullName")]
public string FullName { get; set; }
[XmlElement(ElementName = "Phone")]
public string Phone { get; set; }
}
here is my desirialization code
private object DeserialzeXml(string xml)
{
var xmlSer = new XmlSerializer(typeof(CustomersData), new XmlRootAttribute("CustomerQueryRs"));
var stringReader = new StringReader(xml);
return xmlSer.Deserialize(stringReader);
}
please help......
This should work:
[XmlElement("CustomerRet")]
public List<Customer> Customers { get; set; }
And a full example:
[XmlTypeAttribute(AnonymousType = true)]
public class CustomersData
{
[XmlElement("CustomerRet")]
public List<Customer> Customers { get; set; }
public CustomersData()
{
Customers = new List<Customer>();
}
}
public class Customer
{
[XmlElement(ElementName = "ListID")]
public string ListID { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "FullName")]
public string FullName { get; set; }
[XmlElement(ElementName = "Phone")]
public string Phone { get; set; }
}
class Program
{
static void Main()
{
var xml =
#"<?xml version=""1.0"" ?>
<CustomerQueryRs>
<CustomerRet>
<ListID>6BE0000-1159990808</ListID>
<Name>+ Blaine Bailey</Name>
<FullName>+ Blaine Bailey</FullName>
<Phone>866-855-0800</Phone>
</CustomerRet>
<CustomerRet>
<ListID>9BA0000-1165353294</ListID>
<Name>+ Brian Boyd</Name>
<FullName>+ Brian Boyd</FullName>
<Phone>203-245-1877</Phone>
</CustomerRet>
<CustomerRet>
<ListID>9280000-1164147562</ListID>
<Name>+ Brian Leahy</Name>
<FullName>+ Brian Leahy</FullName>
<Phone>508-341-0955</Phone>
</CustomerRet>
</CustomerQueryRs>";
var serializer = new XmlSerializer(typeof(CustomersData), new XmlRootAttribute("CustomerQueryRs"));
using (var stringReader = new StringReader(xml))
using (var reader = XmlReader.Create(stringReader))
{
var result = (CustomersData)serializer.Deserialize(reader);
Console.WriteLine(result.Customers[1].FullName);
}
}
}
An Readable code for converting xml to list
string xmlString = System.IO.File.ReadAllText(#"C:\Users\user\Downloads\userDetail.xml");
List<GridViewDetails> userDetail = (List<GridViewDetails>)ConvertXmlStringtoObject<List<GridViewDetails>>(xmlString);
static T ConvertXmlStringtoObject<T>(string xmlString)
{
T classObject;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader stringReader = new StringReader(xmlString))
{
classObject = (T)xmlSerializer.Deserialize(stringReader);
}
return classObject;
}

Categories