C# populating class array from xml linq results in FormatException - c#

I'm getting a FormatException error "Input string was not in a correct format." error...
Employee class is all strings except the Status(int), Type(int) and Displayed(bool)
public partial class Version2 : System.Web.UI.Page {
private Employee[] Employees;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
XDocument xdoc = XDocument.Load(Server.MapPath("~/")+ "V2Employees.xml");
IEnumerable<XElement> emps = xdoc.Root.Descendants();
Employees = emps.Select(x => new Employee()
{
Employee_ID = x.Attribute("id").Value,
First_Name = x.Element("First_Name").Value,
Last_Name = x.Element("Last_Name").Value,
Classification_Group = x.Element("Classification_Group").Value,
Classification_Level = x.Element("Classification_Level").Value,
Postion_Number = x.Element("Postion_Number").Value,
English_Title = x.Element("English_Title").Value,
French_Title = x.Element("French_Title").Value,
Location = x.Element("Location").Value,
Language = x.Element("Language").Value,
Department_ID = x.Element("Department_ID").Value,
Status = int.Parse(x.Element("Status").ToString()),
Type = int.Parse(x.Element("Type").ToString()),
Displayed = false}).ToArray();
}
Example XML going in:
<?xml version="1.0" encoding="utf-8" ?>
<employees>
<employee id="1">
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
<Classification_Group>AA</Classification_Group>
<Classification_Level>02</Classification_Level>
<Postion_Number>12345</Postion_Number>
<English_Title>Bob</English_Title>
<French_Title></French_Title>
<Location>Null Island</Location>
<Language>English</Language>
<Department_ID>000001</Department_ID>
<Status>1</Status>
<Type>4</Type>
</employee>
</employees>

By calling ToString on an XElement, you're ending up with the whole element as a string, e.g.
<Status>4</Status>
... and trying to parse that as an int. I would strongly suggest:
Just using the Value property or a cast to string when you want the value of an element as a string (no need for a ToString call afterwards)
Using a cast to int when you want to convert an XElement's value to an integer
I would write your Select as:
// Property names adjusted to .NET naming conventions, type of
// PositionNumber changed to an int.
Employees = emps.Select(x => new Employee
{
EmployeeId = (string) x.Attribute("id"),
FirstName = (string) x.Element("First_Name"),
LastName = (string) x.Element("Last_Name"),
ClassificationGroup = (string) x.Element("Classification_Group"),
ClassificationLevel = (string) x.Element("Classification_Level"),
PositionNumber = (int) x.Element("Postion_Number"),
EnglishTitle = (string) x.Element("English_Title"),
FrenchTitle = (string) x.Element("French_Title"),
Location = (string) x.Element("Location"),
Language = (string) x.Element("Language"),
DepartmentId = (string) x.Element("Department_ID"),
Status = (int) x.Element("Status"),
Type = (int) x.Element("Type"),
Displayed = false
}
You might want to use .Value instead of the cast to string, which will mean you get a NullReferenceException if the element is missing the appropriate sub-element. You may want to use the above form instead, then have a Validate method to check that it has all the required information - that would probably allow you to report errors more clearly:
You could say which property is missing rather than just getting an exception
You could report multiple missing properties in a single error message

See code below. The fix was : xdoc.Root.Descendants("employee"); I also made other improvements
public class Employee
{
static Employee[] Employees = null;
const string FILENAME = #"c:\temp\test.xml";
public static Boolean IsPostBack = false;
string Employee_ID { get; set; }
string First_Name { get; set; }
string Last_Name { get; set; }
string Classification_Group { get; set; }
string Classification_Level { get; set; }
string Postion_Number { get; set; }
string English_Title { get; set; }
string French_Title { get; set; }
string Location { get; set; }
string Language { get; set; }
string Department_ID { get; set; }
int Status { get; set; }
int Type { get; set; }
Boolean Displayed { get; set; }
//protected void Page_Load(object sender, EventArgs e)
public void Page_Load()
{
if (!IsPostBack)
{
XDocument xdoc = XDocument.Load(FILENAME);
IEnumerable<XElement> emps = xdoc.Root.Descendants("employee");
Employees = emps.Select(x => new Employee()
{
Employee_ID = (string)x.Attribute("id"),
First_Name = (string)x.Element("First_Name"),
Last_Name = (string)x.Element("Last_Name"),
Classification_Group = (string)x.Element("Classification_Group"),
Classification_Level = (string)x.Element("Classification_Level"),
Postion_Number = (string)x.Element("Postion_Number"),
English_Title = (string)x.Element("English_Title"),
French_Title = (string)x.Element("French_Title"),
Location = (string)x.Element("Location"),
Language = (string)x.Element("Language"),
Department_ID = (string)x.Element("Department_ID"),
Status = (int)x.Element("Status"),
Type = (int)x.Element("Type"),
Displayed = false
}).ToArray();
}
}
}

Related

Cannot convert system.generic.list anonymous type

I have a class I want to populate from a Linq query, but I am using a sub select statement to slightly alter the properties of the list. I have a class it should fit into but it refuses to go in. I am wondering if there is a way I can get these results to fit into the list as I defined it rather than a generic anonymous type.
public class SCADA_DATA_Truncated
{
public string acode { get; set; }
public string LOCCODE { get; set; }
public Nullable<System.DateTime> COLDATE { get; set; }
public string RESULT { get; set; }
public string analyte { get; set; }
}
And here is where I am attempting to populate the data:
List<SCADA_DATA_Truncated> dataResults = (SCADA_DATA_Truncated)(from b in a2Entity.SCADA_DATA
where DbFunctions.TruncateTime(b.COLDATE) >= dateCheck1 && DbFunctions.TruncateTime(b.COLDATE) <= dateCheck2
&& whereInAcode.Contains(b.acode) && whereInLoc.Contains(b.LOCCODE)
select new
{
COLDATE = DbFunctions.TruncateTime(b.COLDATE),
acode = b.acode,
LOCCODE = b.LOCCODE,
RESULT = b.RESULT,
analyte = b.analyte
}
).ToList();
This is an anonymous type:
select new
{
COLDATE = DbFunctions.TruncateTime(b.COLDATE),
acode = b.acode,
LOCCODE = b.LOCCODE,
RESULT = b.RESULT,
analyte = b.analyte
}
The runtime has no idea how to convert it to your class
Why not changing it to
select new SCADA_DATA_Truncated
{
COLDATE = DbFunctions.TruncateTime(b.COLDATE),
acode = b.acode,
LOCCODE = b.LOCCODE,
RESULT = b.RESULT,
analyte = b.analyte
}
You can then remove the explicit cast altogether

Setting properties for object only when present in database

I have an object that is supposed to describe some customer with these values:
{
public class CustomerDescription
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
public string StreetName { get; set; } = "";
public string HouseNumber { get; set; } = "";
public string PostalPlace { get; set; } = "";
public string PostCode { get; set; } = "";
public string CountryCode { get; set; } = "";
public string EMail { get; set; } = "";
public string PhoneNumber { get; set; } = "";
}
}
These values are retrieved from a database of customers and will be used to create an XML file which will be sent via SOAP to another database. However, not all these values are always present. For example one customer may not have the country code in the database. In this case I would like to not include this value at all in the XML file I am sending. Here is an example of how I create the XML elements:
new XElement("address",
new XElement("address1", CustomerDescription.StreetName + " " + cabCustomerDescription.HouseNumber),
new XElement("postalCode", cUstomerDescription.PostCode),
new XElement("city", customerDescription.PostalPlace),
new XElement("countryCode", customerDescription.CountryCode))
)
This is done for all the properties in CustomerDescription. My question is, how can I do this so that if a value is not present in the database, then this value is not included in the XML file? I would not like it to be empty, like <countryCode></countryCode>, but rather not present at all.
You just need to use ternary operator if value is null from database dont include in x element.
Ex:Explicitely i did StreetName null an dcheck if null then dont add in xml file.
CustomerDescription t = new CustomerDescription();
t.StreetName = null;
var abc = new XElement("address", t.StreetName != null ? new XElement("address1", t.StreetName + " " + t.HouseNumber) : null,
new XElement("postalCode", t.PostCode),
new XElement("city", t.PostalPlace),
new XElement("countryCode", t.CountryCode));

RESTful - WCF - Object reference not set to an instance of an object

I created a simple RESTful application in WCF(c#). When I'm populating using (GET) I've received this error
"Object reference not set to an instance of an object".
I received the error in the part of target.DocumentLines[0].itemCode = "";.
Here's my code:
public PRRequestData[] getAllPR()
{
List<PRRequestData> list = new List<PRRequestData>();
try
{
string sqlSelect = "SELECT DocEntry, Comments, ReqDate FROM OPRQ";
APP.strCommand = sqlSelect;
DataTable dt = new DataTable();
dt = APP.Ds.Tables[0];
foreach (DataRow row in dt.Rows)
{
// Person target = Activator.CreateInstance();
PRRequestData target = new PRRequestData();
target.requiredDate = row["ReqDate"].ToString();
target.remarks = row["Comments"].ToString();
target.docEntry = row["DocEntry"].ToString();
// DataColumnAttribute.Bind(row,target);
sqlSelect = "SELECT ItemCode, Quantity, Price, VendorNum, TaxCode FROM PRQ1 WHERE DocEntry = '" + row["DocEntry"].ToString() + "' ";
APP.strCommand = sqlSelect;
for (var i = 0; i < APP.Ds.Tables[0].Rows.Count; i++)
{
target.DocumentLines[0].itemCode = "";
}
list.Add(target);
}
return list.ToArray();
}
catch (Exception e)
{
e.ToString();
}
return list.ToArray();
Here's my DataContract source code also:
[DataContract(Namespace = "")]
public class PRRequestData
{
[DataMember]
public string docEntry { get; set; }
[DataMember]
public string remarks { get; set; }
[DataMember]
public string requiredDate { get; set; }
//[DataMember]
//public int rowcount { get; set; }
[DataMember]
public RequestDataDetails[] DocumentLines;
}
[DataContract]
public class RequestDataDetails
{
[DataMember]
public string itemCode { get; set; }
[DataMember]
public decimal quantity { get; set; }
[DataMember]
public decimal price { get; set; }
[DataMember]
public string supplier { get; set; }
[DataMember]
public string taxcode { get; set; }
}
Looks like you're not initialising this property
[DataMember]
public RequestDataDetails[] DocumentLines;
I recommend you to use List instead of RequestDataDetails[], as anyway you'll have to use an internal list.
Initialise the List of RequestDataDetails to fill it from the query, before the for loop.
List<RequestDataDetails> requestDetails = new List<RequestDataDetails>
Then change the for loop to add to that list instead of setting an array, im using the assignment you were doing, not sure if it'll do what you expect, just tell me if it suits your needs.
requestDetails.add(new RequestDataDetails { itemCode = "" });
instead of
target.DocumentLines[0].itemCode = "";
Then after the for loop convert the list to an array and assign it to target
target.DocumentLines = requestDetails.ToArray();
Hope it works!

PrintJob TimeSubmitted how to cast to datetime

I have a C# class that takes job printers and some of its properties including TimeSubmitted, that is stored as DateTime with this format: 20130608204517.699000-300
I defined an object with this properties:
public class PrinterJob
{
public String Document { get; set; }
public string JobName { get; set; }
public UInt32 JobId { get; set; }
public string JobStatus { get; set; }
//===========================================================================
// This property doesn't cast adecuate
//===========================================================================
public Datetime TimeSubmitted { get; set; }
}
When I'll try to cast the DateTime (at Run Time):
foreach (ManagementObject prntJob in prntJobCollection)
{
System.String jobName = prntJob.Properties["Name"].Value.ToString();
//Job name would be of the format [Printer name], [Job ID]
char[] splitArr = new char[1];
splitArr[0] = Convert.ToChar(",");
string prnterName = jobName.Split(splitArr)[0];
if (String.Compare(prnterName, printerName, true) == 0)
{
PrinterJob prtobj = new PrinterJob();
prtobj.Document = prntJob.Properties["Document"].Value.ToString();
prtobj.JobName = prntJob.Properties["Name"].Value.ToString();
var id = prntJob.Properties["JobId"].Value;
prtobj.JobId = (UInt32)prntJob.Properties["JobId"].Value;
prtobj.JobStatus = prntJob.Properties["JobStatus"].Value.ToString();
//============================================================================
// Here The cast doesn't work. It comes as: 20130608204517.699000-300
// It throws an InvalidCastException
//============================================================================
prtobj.TimeSubmitted = (DateTime)prntJob.Properties["TimeSubmitted"].Value;
jobList.Add(prtobj);
}
}
What is the right way to cast this value to convert it to DateTime?
add reference to System.Management
var datetime = ManagementDateTimeConverter.ToDateTime("20130608204517.699000-300");
ManagementDateTimeConverter.ToDateTime
Isnt that a simple datetime? (ie. yyyyMMddHHmmss)
I wouldnt know what the part after the dot means though.

XML Parsing to c# objects

I am trying to do a XML parser which will extract data from a website using REST service, the protocol for communication is HTTP, the data I get is in XML format, and I get to the data I need after several requests to different addresses on the server. I need to parse this data to c# objects so I can operate with them lately.
The information on the server is on 5 levels( I am willing to make work only 4 of them for know)
1-List of vendors
2-List of groups
3-List of subgroups
4-List of products
5-List of full information about products
After I get to 4th level I need to check if the product is in my database or if it has different details so I can add or update it.
With "GET" request to a server I get XML with this structure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vendors>
<vendor>
<id>someID</id>
<name>someName</name>
</vendor>
<vendor>
<id>someId1</id>
<name>somename1</name>
</vendor>
</vendors>
XML structure for groups is the same :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<groups vendor_id="43153185318">
<group>
<id>someID</id>
<name>someName</name>
</group>
<group>
<id>someId1</id>
<name>somename1</name>
</group>
The XML structure is analogical for subgroups and products, except that for products I have more elements like catalog_num, price etc.
I made the classes as follows :
public class VendorList
{
public List<Vendor> vendor_list { get; set; }
public VendorList()
{
vendor_list = new List<Vendor>();
}
}
public class Vendor
{
public string id { get; set; }
public string name { get; set; }
public List<Group> groups_list { get; set; }
public Vendor()
{
id = "N/A";
name = "N/A";
groups_list = new List<Group>();
}
}
public class Group
{
public string id { get; set; }
public string name { get; set; }
public List<SubGroup> subgroup_list { get; set; }
public Group()
{
id = "N/A";
name = "N/A";
subgroup_list = new List<SubGroup>();
}
}
public class SubGroup
{
public string id { get; set; }
public string name { get; set; }
public List<Product> product_list { get; set; }
public SubGroup()
{
id = "N/A";
name = "N/A";
product_list = new List<Product>();
}
}
public class Product
{
public string available { get; set; }
public string catalog_num { get; set; }
public string code { get; set; }
public string currency { get; set; }
public string description { get; set; }
public string haracteristics { get; set; }
public string product_id { get; set; }
public string model { get; set; }
public string name { get; set; }
public string price { get; set; }
public string price_dds { get; set; }
public string picture_url { get; set; }
public Product()
{
available = "N/A";
catalog_num = "N/A";
code = "N/A";
currency = "N/A";
description = "N/A";
haracteristics = "N/A";
product_id = "N/A";
model = "N/A";
name = "N/A";
price = "N/A";
price_dds = "N/A";
picture_url = "N/A";
}
}
and the Parser method like this :
public static void FillVendor(string url)
{
string result = GetXMLstream(url);
var vendors = new VendorList();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba.xml");
XDocument d = XDocument.Load(#"D:/proba/proba.xml");
vendors.vendor_list = (from c in d.Descendants("vendor")
select new Vendor()
{
id = c.Element("id").Value,
name = c.Element("name").Value
}).ToList<Vendor>();
foreach (Vendor v in vendors.vendor_list)
{
FillGroups(v.id);
}
}
public static void FillGroups(string vendorID)
{
string url = "main address" + vendorID;
string result = GetXMLstream(url);
var group = new Vendor();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba1.xml");
XDocument d = XDocument.Load(#"D:/proba/proba1.xml");
group.groups_list = (from g in d.Descendants("group")
select new Group()
{
id = g.Element("id").Value,
name = g.Element("name").Value
}).ToList<Group>();
foreach (Group g in group.groups_list)
{
FillSubGroup(vendorID, g.id);
}
}
public static void FillSubGroup(string vendorID, string groupID)
{
string url = "main address" + vendorID+"/"+groupID;
string result = GetXMLstream(url);
var subgroup = new Group();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba2.xml");
XDocument d = XDocument.Load(#"D:/proba/proba2.xml");
subgroup.subgroup_list = (from g in d.Descendants("subgroup")
select new SubGroup()
{
id = g.Element("id").Value,
name = g.Element("name").Value
}).ToList<SubGroup>();
foreach (SubGroup sb in subgroup.subgroup_list)
{
FillProduct(vendorID, groupID, sb.id);
}
}
public static void FillProduct(string vendorID,string groupID,string subgroupID)
{
string url = "main address" + vendorID + "/" + groupID+"/"+subgroupID;
string result = GetXMLstream(url);
var product = new SubGroup();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba2.xml");
XDocument d = XDocument.Load(#"D:/proba/proba2.xml");
product.product_list = (from g in d.Descendants("subgroup")
select new Product()
{
available = g.Element("available").Value,
catalog_num = g.Element("catalog_num").Value,
code = g.Element("code").Value,
currency = g.Element("currency").Value,
description = g.Element("description").Value,
haracteristics = g.Element("haracteristics").Value,
product_id = g.Element("id").Value,
model = g.Element("model").Value,
name = g.Element("name").Value,
price = g.Element("price").Value,
price_dds = g.Element("price_dds").Value,
picture_url = g.Element("small_picture").Value,
}).ToList<Product>();
}
But after finishing parsing I try to check if my Lists are populated with objects, but I get an error which says that they are null "NullReferenceException"
So my question is did I make classes properly and is my parsing method right ( you can suggest how to parse the xml without creating a file on my computer) and if I didn't where is my mistake and how should I make it work properly?
Thanks in advance!
modify this line add 's'( vendor -> vendors)
-> vendors.vendor_list = (from c in d.Descendants("vendor")
and the same case for group -> groups
Instead of making the classes yourself.
Create a properly formatted XML Schema either manually or with Visual Studio and then from that XSD File you've created generate your C# Classes.

Categories

Resources