Object reference not set to an instance of an object - LINQ - c#

I am totally stumped on this one. I am getting a "Object reference not set to an instance of an object." error - but I can't figure out why. This is the code that I have:
public class PlayerProfile
{
public List<Profile> PlayerINfo = new List<Profile>();
public void LoadProfiles(string path)
{
XDocument xmlDoc = XDocument.Load(path);
PlayerINfo = new List<Profile>();
// This is where I get the error:
PlayerINfo = (from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
select new Profile
{
Name = (string)profiles.Element("Name"),
Sex = (string)profiles.Element("Sex"),
Avatar = (string)profiles.Element("Avatar").Attribute("path") ?? "",
Created = (DateTime)profiles.Element("Created")
}).ToList();
}
}
Here is my Profile class:
public class Profile
{
public string Name { get; set; }
public string Sex { get; set; }
public string Avatar { get; set; }
public DateTime Created { get; set; }
}
EDIT - Adding the XML file code:
<?xml version="1.0" encoding="utf-8"?>
<OnlineProfile>
<Player>
<Name>Stacey</Name>
<Sex>Female</Sex>
<Avatar path="/images/Picture.png" />
<Ratio>
<Win>0</Win>
<Loss>0</Loss>
<Abandoned>0</Abandoned>
</Ratio>
<Created>6/19/2011</Created>
</Player>
</OnlineProfile>

from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
This is the problem - OnlineProfile is your root element, just do
from profiles in xmlDoc.Root.Elements("Player")

Do this: from profiles in xmlDoc.Element("OnlineProfile").Elements("Player") instead of from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
From the XML you posted "OnlineProfile" is your Root element, so the child elements you expect arent there.

Tried this out + solved this in visual studio. I see the people above beat me to it. Another way to achieve this is:
xmlDoc.Element("OnlineProfile").Elements("Player")
This is what I posted prior to the Xml becomming available...
here's a good candidate for that error
(string)profiles.Element("Avatar").Attribute("...
").Attribute would cause an error. You need to check for null.
e.g.
= profiles.Element("Avatar") != null ? (string)profiles.Element("Avatar").Attribute("... : null;
do you definitely have an element called Avatar in your Xml file. Did the file load in correctly?

Related

How can we have dynamic xml element name in C# class file?

This is my current XML Response tag
<Response_Data_1234 diffgr:id="Response_Data_1234" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<Status>File received.</Status>
<Time>2022-01-25T09:44:15.73+08:00</Time>
<Location>HKG</Location>
</Response_Data_1234>
the number 1234 in <Response_Data_1234> is a Response Data id, which will be dynamic depending on the request.
Could someone please me create a C# class in this scenario so that I can map the response directly to the class. Thanks in advance
Actually you should not have any unique identifier attached to XML element name. It should be added as attribute.
To answer your question:
There is no direct class attribute to ignore class name while deserialisation of xml string to object.
Option1 (better): Create mapper extension method for XNode to class attribute mapping and reuse it.
Option2:
If your xml string is small you can try to update name of root element and use it to deserialise.
sample code:
var responseDataXDoc = XDocument.Parse(xml);
responseDataXDoc.Root.Name = "ResponseData";
var serializer = new XmlSerializer(typeof(ResponseData));
var responseData = serializer.Deserialize(new StringReader(responseDataXDoc.ToString(SaveOptions.DisableFormatting)));
With Cinchoo ETL - an open source library, you can deserialize such xml easily as below
using (var r = ChoXmlReader<ResponseData>.LoadText(xml)
.WithXPath("//")
.WithXmlNamespace("diffgr", "")
.WithXmlNamespace("msdata", "")
)
{
var rec = r.FirstOrDefault();
rec.Print();
}
public class ResponseData
{
public string Status { get; set; }
public DateTime Time { get; set; }
public string Location { get; set; }
}
Sample fiddle: https://dotnetfiddle.net/HOOPOg

Load xml file into multidimensional array

I am pulling my hair out trying to read an XML file into a two dimensional array. I really do not know why the error is appearing. As a matter of fact I have not even done the array part yet.
The XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
-<TERMINAL_MESSAGE_FILE>
-<MESSAGE>
<LABEL>LABEL1</LABEL>
<MSG>MESSAGE1</MSG>
</MESSAGE>
-<MESSAGE>
<LABEL>LABEL2</LABEL>
<MSG>MESSAGE2</MSG>
</MESSAGE>
</TERMINAL_MESSAGE_FILE>
All of the element values come from an array, messageArray[2]. It is possible that I am not creating this right as I am quite new to XML.
Now I am getting the file name from a file dialog control. Here is the code I am using:
class message
{
public string _label{ get; set; }
public string _message{ get; set; }
}
public partial class messageForm : Form
{
InitializeComponent();
}
private loadBTN_Click(object sender, EventArgs e)
{
DialogResult result = openMsgFD.ShowDialog();
if (result == DialogResult.OK)
{
XDocument doc = XDocument.Load(openMsgFD.FileName);
var settingsList = (from element in doc.Root.Elements("MESSAGE")
select new message
{
_label = element.Attribute("LABEL").Value,
_message= element.Attribute("MESSAGE").Value
}).ToList();
// will need to deal with putting this into the array but I haven't got that far yet.
}
}
So the error I get is "Object reference not set to an instance of an object."
Thing is I copied this code from numerous examples that I have seen and some slightly different, but I always get this error. I tried using streamReader and streamWriter but there are carriage returns in the MESSGAE.VALUE so I cannot get that to work.
Any help would be much appreciated. Thanks, Tom
As I hinted in the comment, LABEL and MSG are elements, not attributes.
First up, I would re-define message as below - in idiomatic C#, classes and properties are PascalCased:
public class Message
{
public string Label { get; set; }
public string Msg { get; set; }
}
Then this XLinq should work:
var settingsList = (from element in doc.Root.Elements("MESSAGE")
select new Message
{
Label = (string)element.Element("LABEL"),
Msg = (string)element.Element("MSG")
}).ToList();

Getting XML attribute values

How I read in the specific value of an XML attribute from a node when my XML looks like the following:
<Settings>
<Display_Settings>
<Screen>
<Name Name="gadg" />
<ScreenTag Tag="asfa" />
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
I only know how to read in the inner text value of XML and not the attribute value. For instance, I want the value of X in LocalPosition. This is what I've tried so far;
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Screen");
foreach (XmlNode nodeInfo in nodeList)
{
XmlNodeList nodeContent = nodeInfo.ChildNodes;
foreach (XmlNode nodeItems in nodeContent)
{
if (nodeItems.Name == "Tag")
{
print("There is a name");
}
if (nodeItems.Name == "LocalPosition")
{
print("TEST");
}
}
}
Though for what I want to do, I think this is the wrong way to go about it. Can someone point in the right direction please?
You can use LINQ to XML:
var xdoc = XDocument.Load(path_to_xml);
int x = (int)xdoc.Descendants("LocalPosition").First().Attribute("X");
Or with XPath
int x = (int)xdoc.XPathSelectElement("//LocalPosition").Attribute("X");
string XValue= nodeItems.Attributes["X"].Value; // will solve your problem.
I was a little confused when I first started parsing XML with C# too! Instead of trying to write it all yourself .NET provides System.XML.Linq and System.XML.Serialization to help us with all of the hard stuff!
This approach is slightly different as we:
Load the XML document into a System.XML.Linq.XDocument,
Deserialize the XDocument into .NET objects that we can use as we please =]
I hope you don't mind, but I tweaked your XML example a little bit. Try to avoid using too many attributes within your elements, as they tend to reduce readability. The first line of the XML sample just highlights the version that is being used.
You should be able to copy and paste the code sample below into a new console application to get a feel for how it works. Just make sure your XML file is in the ..\bin\Debug folder (otherwise you will need to provide a full file path reference to it).
As you can see, in this example your XML elements (Display_Settings, Screen, Name, ScreenTag, and LocalPosition) are all classes that we decorate with XMLElement attributes. These classes just have public properties that are automatically populated when we call Deserialize(). Neat!
The same applies to the X, Y and Z attributes.
There are a bunch of tutorials that can explain this a lot better than me - enjoy! =]
XML Example
<?xml version="1.0"?>
<Settings>
<Display_Settings>
<Screen>
<Name>NameGoesHere</Name>
<ScreenTag>ScreenTagGoesHere</ScreenTag>
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
Complete Code Sample
using System;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create XDocument to represent our XML file
XDocument xdoc = XDocument.Load("XmlFile.xml");
// Create a deserializer and break down our XML into c# objects
XmlSerializer deserializer = new XmlSerializer(typeof(Settings));
// Deserialize into a Settings object
Settings settings = (Settings)deserializer.Deserialize(xdoc.CreateReader());
// Check that we have some display settings
if (null != settings.DisplaySettings)
{
Screen screen = settings.DisplaySettings.Screen;
LocalPosition localPosition = screen.LocalPosition;
// Check to make sure we have a screen tag before using it
if (null != screen.ScreenTag)
{
Console.WriteLine("There is a name: " + screen.ScreenTag);
}
// We can just write our integers to the console, as we will get a document error when we
// try to parse the document if they are not provided!
Console.WriteLine("Position: " + localPosition.X + ", " + localPosition.Y + ", " + localPosition.Z);
}
Console.ReadLine();
}
}
[XmlRoot("Settings")]
public class Settings
{
[XmlElement("Display_Settings")]
public DisplaySettings DisplaySettings { get; set; }
}
public class DisplaySettings
{
[XmlElement("Screen")]
public Screen Screen { get; set; }
}
public class Screen
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("ScreenTag")]
public string ScreenTag { get; set; }
[XmlElement("LocalPosition")]
public LocalPosition LocalPosition { get; set; }
}
public class LocalPosition
{
[XmlAttribute("X")]
public int X { get; set; }
[XmlAttribute("Y")]
public int Y { get; set; }
[XmlAttribute("Z")]
public int Z { get; set; }
}
}
Try with nodeItems.Attributes["X"].Value

Deserializing XML where each object has a list of objects

I'm working on a WP7 app which gets and updates data on a web server. If any updates need a response, I get a list of errors that needs to be dealt with, and a list of possible choices for each error. The issue I'm having is assigning each object its appropriate list of choices. As of now I get a list of errors, and another list of all possible choices for all errors. I'd like the error object to contain the list of only its options so I can handle that.
So here's an example response:
<?xml version="1.0" encoding="utf-8"?>
<response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<response_error_dialogs>
<error_dialog_list>
<error_dialog_choice>
<error_dialog_id>1301</error_dialog_id>
<error_dialog_message>You have changed the phone number. Select which phone number to make the primary contact number.</error_dialog_message>
<error_dialog_title>Phone Number Changed</error_dialog_title>
<error_dialog_is_set>false</error_dialog_is_set>
<error_dialog_choice_option_list>
<error_dialog_choice_option>
<error_dialog_choice_option_id>1</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Home</error_dialog_choice_option_title>
</error_dialog_choice_option>
<error_dialog_choice_option>
<error_dialog_choice_option_id>2</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Mobile</error_dialog_choice_option_title>
</error_dialog_choice_option>
<error_dialog_choice_option>
<error_dialog_choice_option_id>3</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Work</error_dialog_choice_option_title>
</error_dialog_choice_option>
</error_dialog_choice_option_list>
</error_dialog_choice>
<error_dialog_choice>
<error_dialog_id>1303</error_dialog_id>
<error_dialog_message>You have changed the account email address. Would you like this to be the new default email?</error_dialog_message>
<error_dialog_title>Email Address Changed</error_dialog_title>
<error_dialog_is_set>false</error_dialog_is_set>
<error_dialog_choice_option_list>
<error_dialog_choice_option>
<error_dialog_choice_option_id>1</error_dialog_choice_option_id>
<error_dialog_choice_option_title>No</error_dialog_choice_option_title>
</error_dialog_choice_option>
<error_dialog_choice_option>
<error_dialog_choice_option_id>2</error_dialog_choice_option_id>
<error_dialog_choice_option_title>Yes</error_dialog_choice_option_title>
</error_dialog_choice_option>
</error_dialog_choice_option_list>
</error_dialog_choice>
</error_dialog_list>
</response_error_dialogs>
</response>
And here's the classes used:
public class ErrorDialog
{
XElement self;
public ErrorDialog() { }
public ErrorDialog(XElement errorDialog)
{
self = errorDialog;
}
public int errorDialogId
{
get { return (int)(self.Element("error_dialog_id")); }
}
public string errorDialogMessage
{
get { return (string)(self.Element("error_dialog_message")); }
}
public string errorDialogTitle
{
get { return (string)(self.Element("error_dialog_title")); }
}
public bool errorDialogIsSet
{
get { return (bool)(self.Element("error_dialog_is_set")); }
}
public List<ErrorDialogChoice> errorDialogChoice
{
get { return (List<ErrorDialogChoice>)(errorDialogChoice); }
}
public int errorDialogSelectedOption
{
get { return (int)(self.Element("error_dialog_selected_option")); }
}
}
class ErrorDialogChoice
{
XElement self;
public ErrorDialogChoice() { }
public ErrorDialogChoice(XElement errorDialogChoice)
{
self = errorDialogChoice;
}
public int errorDialogChoiceOptionId
{
get { return (int)(self.Element("error_dialog_choice_option_id")); }
}
public string errorDialogChoiceOptionTitle
{
get { return (string)(self.Element("error_dialog_choice_option_title")); }
}
}
And here's how I'm parsing it:
XElement response = XElement.Parse(data);
ErrorDialog[] dialogs = response
.Element("response_error_dialogs")
.Element("error_dialog_list")
.Elements("error_dialog_choice")
.Select(e => new ErrorDialog(e))
.ToArray();
ErrorDialogChoice[] edChoices = response
.Element("response_error_dialogs")
.Element("error_dialog_list")
.Element("error_dialog_choice")
.Element("error_dialog_choice_option_list")
.Elements("error_dialog_choice_option")
.Select(e => new ErrorDialogChoice(e))
.ToArray();
So with this example, the first error_dialog_choice object will have a List containing 3 error_dialog_choice_option objects, the second has the two error_dialog_choice_option objects, and any more that may come back. Any help is appreciated. Thanks.
You can use XML serialization to achieve this much easier:
var reader = new StringReader(xmlString);
var ser = new XmlSerializer(typeof(Response));
var result = (Response) ser.Deserialize(reader);
Using these class definitions.
[XmlType("response")]
public class Response
{
[XmlElement("response_error_dialogs")]
public ErrorDialog ErrorDialog;
}
[XmlType("response_error_dialogs")]
public class ErrorDialog
{
[XmlArray("error_dialog_list")]
public List<ChoiceErrorDialog> ChoiceList;
}
[XmlType("error_dialog_choice")]
public class ChoiceErrorDialog
{
[XmlElement("error_dialog_id")]
public int Id;
[XmlElement("error_dialog_message")]
public string Message;
[XmlElement("error_dialog_title")]
public string Title;
[XmlElement("error_dialog_is_set")]
public bool IsSet;
[XmlArray("error_dialog_choice_option_list")]
public List<Option> OptionList;
}
[XmlType("error_dialog_choice_option")]
public class Option
{
[XmlElement("error_dialog_choice_option_id")]
public int Id;
[XmlElement("error_dialog_choice_option_title")]
public string Title;
}
I am guessing there can be more types of error dialogs, and <error_dialog_choice> is just one of the possible types. In this case you could use subclassing, and list the subclasses with XmlArrayItem attributes.
You could also generate the class definitions with xsd.exe or svcutil.exe, from an .xsd or .wsdl file. xsd.exe can even infer the schema from a sample .xml file.
xsd.exe /?
svcutil.exe /?
Use the XmlSerializer built into the framework. Use attributes to map out the xml equivalent of your classes and their properties.
Example:
[XmlElement("Txn")]
public List<Transaction> Items { get; set; }
You can use the XSD.exe to generate an XSD and then generate C# code for your XSD. Use Visual Studio Command Prompt with a sample response.xml file. Eg:
c:>xsd response.xml
c:>xsd response.xsd /c /edb

stuck binding xml to Model Class

I am experimenting with using xml as a database for small CMS, like gallery or staff profiles
etc
however being all subsonic minded i am stuck on how i bind my xml document to a modelclass
so that i can then use that class for strongly typed views:
here is my model class:
[XmlRoot("employee")]
public class EmployeesModel
{
[Required]
[DisplayName("Name: ")]
[XmlElement("employeeName")]
public string employeeName { get; set; }
[Required]
[DisplayName("Position: ")]
[XmlElement("employeePosition")]
public string employeePosition { get; set; }
[Required]
[DisplayName("Description")]
[XmlElement("employeeDescription")]
public string employeeDescription { get; set; }
[Required]
[DisplayName("Photo: ")]
[XmlElement("employeePhoto")]
public string employeePhoto { get; set; }
[Required]
[DisplayName("ID: ")]
[XmlElement("employeeID")]
public int employeeID { get; set; }
}
and here is my code:
XDocument xmlDoc = XDocument.Load(Server.MapPath("~/App_Data/employees.xml"));
var model = (from xml in xmlDoc.Descendants("employee")
select xml) as IEnumerable<EmployeesModel>;
return View(model);
the XML
<?xml version="1.0" encoding="utf-8" ?>
<employees>
<employee>
<employeeName>David parker</employeeName>
<employeePosition>Senior Web Developer</employeePosition>
<employeeDescription>This is a test description<br>feele free to add something here.</employeeDescription>
<employeePhoto>mypic.jpg</employeePhoto>
<employeeID>1</employeeID></employee></employees>
the xml side work but model is always empty, however i get no runtime erros when trying to bind, i know there is more i should do here but i need some help.
for clarity i am using asp.net mvc 2 rc 2
thanks
You need to deserialize the XML into objects. You cannot simply cast XML as objects. When you say as IEnumerable<EmployeesModel>, you'll get a null since the types aren't compatible. Your code could look something like this:
var serializer = new XmlSerializer(typeof(EmployeesModel));
var model =
from xml in xmlDoc.Descendants("employee")
select serializer.Deserialize(xml.CreateReader()) as EmployeesModel;
Another option you could consider is to project the XElements into EmployeesModel objects, like this:
var model =
from xml in xmlDoc.Descendants("employee")
select new EmployeesModel {
employeeName = (string)xml.Element("employeeName"),
employeePosition = (string)xml.Element("employeePosition"),
employeeDescription = (string)xml.Element("employeeDescription"),
employeePhoto = (string)xml.Element("employeePhoto"),
employeeID = (int)xml.Element("employeeID"), };
As you can see, that can get tedious. However, it may be appropriate. If your XML file represents all employee data but your view shows only a subset of the data or the data in a different structure, you probably don't want your view model to be a direct copy of the contents of your data store.
If you want make binding xml to model class, you can use templay on codeplex.
Further you can do some process on your model class and their childrens.
https://templay.codeplex.com/

Categories