I'm trying to deserialize a given XML File into an object structure.
This is the XML:
<StaticData>
<Configuration>
<SqlCmdParameters />
<Tables>
<Table Schema="dbo" Name="table1">
<Columns>
<Column Name="tab1col1" IsPrimaryKey="false" />
<Column Name="tab1col2" IsPrimaryKey="false" />
</Columns>
</Table>
<Table Schema="dbo" Name="table2">
<Columns>
<Column Name="tab2col1" IsPrimaryKey="false" />
<Column Name="tab2col2" IsPrimaryKey="false" />
</Columns>
</Table>
</Tables>
</Configuration>
<TableDataItems>
<TableData Schema="dbo" Name="table1">
<RowData tab1col1="11" tab1col2="text1" tab1col3="anotherText1" />
<RowData tab1col1="12" tab1col2="text2" tab1col3="anotherText2"/>
<RowData tab1col1="13" tab1col2="text3" tab1col3="anotherText3"/>
</TableData>
<TableData Schema="dbo" Name="table2">
<RowData tab2col1="22" tab2col2="text1" />
<RowData tab2col1="23" tab2col2="text1" />
<RowData tab2col1="24" tab2col2="text1" />
</TableData>
</TableDataItems>
</StaticData>
The classes I want to fill them in are:
[XmlRoot("StaticData")]
public class StaticData
{
[XmlElement("Configuration")]
public Configuration Configuration { get; set; }
[XmlElement("TableDataItems")]
public TableDataItems TableDataItems { get; set; }
}
public class Configuration
{
[XmlElement("Tables")]
public List<Table> Tables { get; set; }
[XmlElement("SqlCmdParameters")]
public List<SqlCommandParameter> SqlCommandParameters { get; set; }
}
public class TableDataItems
{
[XmlElement("TableData")]
public List<Table> TableDatas { get; set; }
}
public class Table
{
[XmlAttribute("Name")]
public string TableName { get; set; }
[XmlAttribute("Schema")]
public string SchemaName { get; set; }
[XmlElement("Columns")]
public List<Column> Columns { get; set; }
[XmlElement("RowData")]
public List<Row> Rows { get; set; }
public Table()
{
Columns = new List<Column>();
Rows = new List<Row>();
}
}
public class Column
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlAttribute("IsPrimaryKey")]
public bool IsPrimaryKey { get; set; }
}
public class Row
{
public Row()
{
RowData = new Dictionary<string, string>();
}
???What Attribute should I put here???
public Dictionary<string, string> RowData { get; set; }
}
So, everything works fine until I get to the where all the Attributes should be filled into a dictionary.
This is how I deserialize the XML so far:
public void CreateObjectStructureFromXml()
{
using (TextReader textReader = new StringReader(XmlDocument.ToString()))
{
XmlSerializer serializer = new XmlSerializer(typeof(StaticData));
StaticData = (StaticData) serializer.Deserialize(textReader);
}
}
I get an exception as soon as I get to the Row elements.
Can anybody please point me to where I made the mistake or what I should do?
The XML RowData can come with a variable amount of attributes. The attributes are the content of a database table.
Thanks a lot in advance
Try xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Table.tables = doc.Descendants("Table").Select(x => new Table() {
SchemaName = (string)x.Attribute("Schema"),
TableName = (string)x.Attribute("Name"),
Columns = x.Descendants("Column").Select(y => new Column()
{
Name = (string)y.Attribute("Name"),
IsPrimaryKey = (Boolean)y.Attribute("IsPrimaryKey")
}).ToList()
}).ToList();
Dictionary<string, XElement> tableData = doc.Descendants("TableData")
.GroupBy(x => (string)x.Attribute("Name"), y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
foreach(Table table in Table.tables)
{
XElement xTable = tableData[table.TableName];
table.SchemaName = (string)xTable.Attribute("Schema");
table.Rows = xTable.Elements("RowData").Select(x => new Row() {
RowData = x.Attributes()
.GroupBy(y => y.Name.LocalName, z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).ToList();
}
}
}
public class Table
{
public static List<Table> tables = new List<Table>();
public string TableName { get; set; }
public string SchemaName { get; set; }
public List<Column> Columns { get; set; }
public List<Row> Rows { get; set; }
public Table()
{
Columns = new List<Column>();
Rows = new List<Row>();
}
}
public class Column
{
public string Name { get; set; }
public bool IsPrimaryKey { get; set; }
}
public class Row
{
public Dictionary<string, string> RowData { get; set; }
}
}
Related
I have an XML file that I am parsing through and have come across a specific element that has it's own child nodes. The XML file is below:
<status.AppleSettings>
<AppleInstance MaxCost="250" Status="77" NewMode="5" SharePrice="350"
FlagTF="False" TimeClock="0" TimeClockSec="14"
Options="7532890" ID="JK_7755" Owner="SLP90"
Server="PA.SL90.COL" Name="SLP90" GroupName="COL.PA"
Instance="AppleServiceInstance" NewFlag="True" FinalCount="0"/>
<AppleInstance MaxCost="5" Status="0" NewMode="1" SharePrice="0"
FlagTF="False" TimeClock="300" TimeClockSec="1000"
Options="56794577431" Owner="A.CON" Instance="SL91"
NewFlag="True" FinalCount="1" List="1450, 1430"
Keyrepo="SYSTEMSERVER_7671902"/>
</status.AppleSettings>
As you can see, there's a parent node - AppleSettings and then two child nodes w/ the same name - AppleInstance. I created two separate classes for the child nodes since they have different attributes. I am able to access AppleSettings and when I do a quickwatch I can see the two child nodes inside, I just can't figure out how to access them. New to XML parsing and C# so everything is trial and error, learning a lot from stackoverflow.
Here is the code I have to access the AppleSettings parent node:
private List<Data> GetAppleSettingsNode(List<XmlDocument> XMLdocument, List<Data> DataList)
{
for (int i = 0; i < XMLdocument.Count(); i++)
{
AppleSettings temp = new AppleSettings();
var temp2 = XMLdocument[i].DocumentElement.SelectNodes("//AppleSettings");
foreach (var node in temp2)
{
var temp3 = node.ToString();
}
XmlNode xmlNode1 = XMLdocument[i].SelectSingleNode("//AppleSettings");
XmlSerializer serial1 = new XmlSerializer(typeof(AppleSettings));
temp = (AppleSettings)serial1.Deserialize(new XmlNodeReader(xmlNode1));
}
}
Is it even necessry to access the AppleSettings node? Could I go directly to the two AppleInstance child nodes? If someone could help me out, I'd appreciate it. Thanks!
It is better to use LINQ to XML API.
(1) The code below shows how to access any XML attribute by its name.
(2) .FirstOrDefault()?.Value technique will prevent exceptions generation when an attribute is missing.
c#
void Main()
{
const string FILENAME = #"e:\Temp\AppleSettings.xml";
XDocument doc = XDocument.Load(FILENAME);
foreach (var el in doc.Descendants("AppleInstance"))
{
Console.WriteLine("MaxCost={0}", el.Attributes("MaxCost").FirstOrDefault()?.Value);
Console.WriteLine("Instance={0}", el.Attributes("Instance").FirstOrDefault()?.Value);
Console.WriteLine("GroupName={0}", el.Attributes("GroupName").FirstOrDefault()?.Value);
}
}
Try following :
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(Root));
Root root = (Root)serializer.Deserialize(reader);
}
}
public class Root
{
[XmlArray("status.AppleSettings")]
[XmlArrayItem("AppleInstance")]
public List<ApppleInstance> AppleInstances { get; set; }
}
public class ApppleInstance
{
[XmlAttribute()]
public int MaxCost { get; set; }
[XmlAttribute()]
public int Status { get; set; }
[XmlAttribute()]
public int NewMode { get; set; }
[XmlAttribute()]
public int SharePrice { get; set; }
private Boolean _FlagTF { get; set; }
[XmlAttribute()]
public string FlagTF
{
get { return _FlagTF? "True" : "False";}
set { _FlagTF = (value == "True") ? true : false;}
}
[XmlAttribute()]
public int TimeClock { get; set; }
[XmlAttribute()]
public int TimeClockSec { get; set; }
[XmlAttribute()]
public long Options { get; set; }
[XmlAttribute()]
public string ID { get; set; }
[XmlAttribute()]
public string Owner { get; set; }
[XmlAttribute()]
public string Server { get; set; }
[XmlAttribute()]
public string Name { get; set; }
[XmlAttribute()]
public string GroupName { get; set; }
[XmlAttribute()]
public string Instance { get; set; }
private Boolean _NewFlag { get; set; }
[XmlAttribute()]
public string NewFlag
{
get { return _NewFlag ? "True" : "False"; }
set { _NewFlag = (value == "True") ? true : false; }
}
[XmlAttribute()]
public int FinalCount { get; set; }
private int[] _List { get; set; }
[XmlAttribute()]
public string List
{
get { return string.Join(",",_List);}
set { _List = value.Split(new char[] {','}).Select(x => int.Parse(x)).ToArray() ;}
}
[XmlAttribute()]
public string Keyrepo { get; set; }
}
}
The i of LargeXMLResponse[i] is loop count of the Large XML response if your response tag in wrapped up in between, and if you have a alternative way please go ahead with.
var ResponseToList = LargeXMLResponse[i].Descendants("Response").ToList();
if (ResponseToList.Count() > 0){
for (var pf = 0; pf < ResponseToList.Count(); pf++)
{
ResponseInfoList.Add(new ResponseToList{
id = ResponseToList[pf].Descendants("Block").Attributes("id").Count() > 0 ?
ResponseToList[pf].Descendants("Block ").Attributes("id").First().Value : "",
});
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I got a class which represents a soccerplayer:
public class PlayerExtended
{
[XmlAttribute("id")] public string Id { get; set; }
[XmlAttribute("shortName")] public string ShortName { get; set; }
[XmlAttribute("firstName")] public string FirstName { get; set; }
[XmlAttribute("surName")] public string SurName { get; set; }
[XmlAttribute("shirtNumber")] public string ShirtNumber { get; set; }
[XmlAttribute("actions")] public string Actions { get; set; }
[XmlAttribute("substitude")] public string Substitude { get; set; }
[XmlAttribute("grade")] public string Grade { get; set; }
[XmlAttribute("iconSmall")] public string IconSmall { get; set; }
[XmlAttribute("position")] public string Position { get; set; }
[XmlAttribute("squadPositionID")] public string SquadPositionId { get; set; }
[XmlAttribute("squadPosition")] public string SquadPosition { get; set; }
[XmlAttribute("inMinute")] public string InMinute { get; set; }
[XmlAttribute("outMinute")] public string OutMinute { get; set; }
[XmlAttribute("captain")] public string Captain { get; set; }
}
After assigning values to the properties one of the players looks like this:
The property "Actions" is an empty string (NOT NULL).
If I serialize it it looks like this:
<player id="51641" shortName="Bürki" firstName="Roman" surName="Bürki" shirtNumber="1" substitude="starter" grade="2,5" iconSmall="xxx.whatever.com" position="11" squadPositionID="1" squadPosition="Torwart"/>
But I want it to look like this:
<player id="51641" shortName="Bürki" firstName="Roman" surName="Bürki" shirtNumber="1" actions="" substitude="starter" grade="2,5" iconSmall="xxx.whatever.com" position="11" squadPositionID="1" squadPosition="Torwart"/>
So how do I serialize an XmlAttribute which is an empty string?
How are you generating your XML? I cannot seem to reproduce your issue.
public class Program
{
public static void Main(string[] args)
{
using var writer = new StringWriter();
var serializer = new XmlSerializer(typeof(Player));
serializer.Serialize(writer, new Player { Name = "", Age = 25 });
Console.WriteLine(writer);
}
}
public class Player
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlAttribute("age")]
public int Age { get; set; }
}
The code above results in the name attribute in the format you desire (name=""). Let me know if this answer is sufficient for you.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;
// Runtime Target = .NET Core v2.1 or .NET Core v3.1
namespace XmlSerialize
{
class Program
{
static void Main(string[] args)
{
var mickey = new Employee { FirstName = "Mickey", LastName = "Mouse" };
var asterix = new Employee { FirstName = "Asterix", LastName = "" };
var obelix = new Employee { FirstName = "Obelix", LastName = null };
var nixnix = new Employee { FirstName = null, LastName = null };
Console.WriteLine(SerializeXml(mickey) + SerializeXml(asterix) + SerializeXml(obelix) + SerializeXml(nixnix));
}
public static string SerializeXml<T>(T instanceToSerialize)
{
var serializer = new XmlSerializer(instanceToSerialize.GetType(), string.Empty);
var result = string.Empty;
using (var stringWriter = new StringWriter())
{
serializer.Serialize(stringWriter, instanceToSerialize);
result = stringWriter.ToString();
}
return result;
}
}
[XmlRoot("Employee")]
public sealed class Employee
{
[XmlAttribute("FirstName")]
public string FirstName { get; set; }
[XmlIgnore]
public string LastName { get; set; }
[XmlAttribute("LastName")]
public string SerializableLastName // <------------ Might this help?
{
get { return this.LastName ?? string.Empty; }
set { this.LastName = value; }
}
[XmlElement]
public List<string> Skills { get; set; }
}
}
Output
<?xml version="1.0" encoding="utf-16"?>
<Employee FirstName="Mickey" LastName="Mouse" />
<Employee FirstName="Asterix" LastName="" />
<Employee FirstName="Obelix" LastName="" />
<Employee LastName="" />
setting the property value to string.Empty will do the trick. I am using XmlSerializer to convert the object to XML. If I set the property to string.Empty, this will result as empty attribute in XML. Here is the example
public class TestClass
{
[XmlAttribute("test1")]
public string test1 { get; set; }
[XmlAttribute("test2")]
public string test2 { get; set; }
}
var dd = new List<TestClass>();
dd.Add( new TestClass() { test1 = "asdf", test2 = string.Empty }); //will generate empty attribute for test2
dd.Add( new TestClass() { test1 = "asdf" }); //the attribute test2 will be ignored
using (var stringwriter = new System.IO.StringWriter())
{
var serializer = new XmlSerializer(dd.GetType());
serializer.Serialize(stringwriter, dd);
Console.WriteLine( stringwriter.ToString());
}
Output
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfTestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TestClass test1="asdf" test2="" />
<TestClass test1="asdf" />
</ArrayOfTestClass>
Imagine a collection of EF dbSet like this :
public class Employee {
...
public string FirstName { get; set; }
public List<Badge> Badge { get; set; }
}
public class Badge {
public long CSN { get; set; }
public int EmployeeId { get; set; }
public int Type { get; set; }
}
This models are used in my SGBB and I want to use it to import data from a CSV file. But this file has a small difference. It give only one badge like this :
FIRSTNAME;CSN;TYPE
Jerome;12345;1
I have used a CollectionGenericConverter, to initialize the List with a new record.
Map(m => m.Firstname).Name("Firstname");
Map(m => m.Badges).Name("CSN").TypeConverter<BadgeConverter>();
...
public class BadgeConverter : CollectionGenericConverter {
public override object ConvertFromString(String text, IReaderRow row, MemberMapData memberMapData) {
return new List<Badge> {
new Badge {
CSN = Convert.ToInt16(text)
}
};
}
}
I have just a problem with the second value, using a second converter reset the list of badges :
Map(m => m.Badges).Name("Type").TypeConverter<AnotherOneBadgeConverter>();
And set directly the first item not work :
Map(m => m.Badges[0].Type).Name("Type");
How to do that ?
Something like this may work for you.
public class Program
{
public static void Main(string[] args)
{
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream))
using (StreamReader reader = new StreamReader(stream))
using (CsvReader csv = new CsvReader(reader))
{
writer.WriteLine("FIRSTNAME;CSN;TYPE");
writer.WriteLine("Jerome;12345;1");
writer.Flush();
stream.Position = 0;
csv.Configuration.Delimiter = ";";
csv.Configuration.RegisterClassMap<EmployeeMap>();
var records = csv.GetRecords<Employee>().ToList();
}
}
}
public class Employee
{
public string FirstName { get; set; }
public List<Badge> Badge { get; set; }
}
public class Badge
{
public long CSN { get; set; }
public int EmployeeId { get; set; }
public int Type { get; set; }
}
public class EmployeeMap: ClassMap<Employee>
{
public EmployeeMap()
{
Map(m => m.FirstName).Name("FIRSTNAME");
Map(m => m.Badge).ConvertUsing(row =>
{
var list = new List<Badge>
{
new Badge { CSN = row.GetField<long>("CSN"), Type = row.GetField<int>("TYPE") },
};
return list;
});
}
}
I am trying to deserialize an XML file within a C# program that looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Addresses>
<ListName>Flowers</ListName>
<Address contextRef="RP.CC">Some Address</Address>
<Address contextRef="RP.BE">Some Other Address</Address>
<Address contextRef="RP.BV">Yet Another Address</Address>
<Address contextRef="RP.CAL">Wow, I Can't Believe It's Another Address</Address>
</Addresses>
I do not have any control over the format of this file. But, it will always have some combination of these 4 Address elements (i.e. these 4 contextRef attribute values are the only ones used) with differing element values each time.
Now, instead of deserializing into an Address array, I need to send them to individual properties within an Addresses object. My Current implementation uses an array and then a setter method to set these properties based on the contextRef as so:
public class Addresses
{
[XmlElement("ListName")]
public string ListName { get; set; }
private Address[] _addresses;
[XmlElement("Address")]
public Address[] AddressesArray
{
get
{
return _addresses;
}
set
{
_addresses = value;
SetAddress();
}
}
[XmlIgnore]
public Address AddressG21 { get; set; }
[XmlIgnore]
public Address AddressG22 { get; set; }
[XmlIgnore]
public Address AddressG23 { get; set; }
[XmlIgnore]
public Address AddressG9 { get; set; }
private void SetAddress()
{
foreach (var address in _addresses)
{
if (address.ContextRef == "RP.CC")
{
AddressG21 = address;
}
else if (address.ContextRef == "RP.BE")
{
AddressG22 = address;
}
else if (address.ContextRef == "RP.BV")
{
AddressG23 = address;
}
else if (address.ContextRef == "RP.CAL")
{
AddressG9 = address;
}
}
}
}
Where the Address object is defined as so:
public class Address
{
private string valueField;
/// <remarks/>
[XmlText]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
[XmlAttribute("contextRef")]
public string ContextRef { get; set; }
}
So, my question is, is there a neater/better way of deserializing this XML directly into the AddressG21, etc. object properties without first using the Address array?
Thanks in advance.
I would use xml linq and create a dictionary in the class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Addresses addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).FirstOrDefault();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}
If you had multiple Addresses elements then use this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Addresses> addresses = doc.Descendants("Addresses").Select(x => new Addresses() {
ListName = (string)x.Element("ListName"),
dict = x.Elements("Address")
.GroupBy(y => (string)y.Attribute("contextRef"), z => (string)z)
.ToDictionary(y => y.Key, z => z.FirstOrDefault())
}).ToList();
}
}
public class Addresses
{
public string ListName { get; set; }
public Dictionary<string, string> dict { get; set; }
}
}
I am trying to create a function to parse an XML file like this:
<?xml version="1.0" encoding="utf-8"?>
<list name="Grocery List" author="Ian" desc="Saturday grocery list">
<item color="black" done="false">Milk</item>
<item color="black" done="false">Eggs</item>
<item color="blue" done="false">Water</item>
</list>
It parses the attributes correctly, but it fails to return the values of the list items. Here is the function and class it uses:
class List
{
public string[] listItems;
public string[] colorArray;
public string[] doneArray;
public string listName;
public string listAuthor;
public string listDesc;
public string err;
}
Reader definition:
class ListReader
{
public List doListParse(string filename)
{
List l = new List();
int arrayCount = 0;
try
{
XmlReader r = XmlReader.Create(filename);
while (r.Read())
{
if (r.NodeType == XmlNodeType.Element && r.Name == "list")
{
//Get the attributes of the list
l.listName = r.GetAttribute("name");
l.listAuthor = r.GetAttribute("author");
l.listDesc = r.GetAttribute("desc");
while (r.NodeType != XmlNodeType.EndElement)
{
r.Read();
if (r.Name == "item")
{
r.Read();
if (r.NodeType == XmlNodeType.Text)
{
//Get The Attributes
l.colorArray[arrayCount] = r.GetAttribute("color");
l.doneArray[arrayCount] = r.GetAttribute("done");
//Get The Content
l.listItems[arrayCount] = r.Value.ToString();
arrayCount++;
}
r.Read();
}
}
}
}
}
catch (Exception e)
{
l.err = e.ToString();
}
return l;
}
}
When I execute the program, it gives this exception:
System.NullReferenceException: Object reference not set to an instance of an object.
What is going on here?
I'd recommend you using a serializer. The XmlSerializer class is pretty decent. It will simplify your code.
So start by defining the models that will map to this XML structure:
[XmlRoot("list")]
public class GroceryList
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlAttribute("author")]
public string Author { get; set; }
[XmlAttribute("desc")]
public string Description { get; set; }
[XmlElement("item")]
public Item[] Items { get; set; }
}
public class Item
{
[XmlAttribute("color")]
public string Color { get; set; }
[XmlAttribute("done")]
public bool Done { get; set; }
[XmlText]
public string Value { get; set; }
}
and then simply deserialize the XML:
class Program
{
static void Main()
{
var serializer = new XmlSerializer(typeof(GroceryList));
using (var reader = XmlReader.Create("groceriesList.xml"))
{
var list = (GroceryList)serializer.Deserialize(reader);
// you could access the list items here
}
}
}
You can use Linq To Xml.
var xElem = XDocument.Parse(xml).Element("list"); //or XDocument.Load(filename)
var list = new
{
Name = xElem.Attribute("name").Value,
Author = xElem.Attribute("author").Value,
Desc = xElem.Attribute("desc").Value,
Items = xElem.Elements("item")
.Select(e => new{
Color = e.Attribute("color").Value,
Done = (bool)e.Attribute("done"),
Value = e.Value,
})
.ToList()
};