Reputation: 1683
I've looked through the various questions on here similar to this, but can't get my solution to work.
I'm using Visual Studio 2015 Community, building a WPF project.
I get xml from my backend API, and I'm trying to convert it into a C# object, but I can't get it to work.
This is the xml
<response>
<computer_setting id="1" hospital_name="foo" computer_type="bar" environment="staging" label_printer_name="labels" document_printer_name="docs"/>
</response>
This is class
using System.Xml.Serialization;
namespace Casechek.Kiosk
{
[XmlRoot("response")]
public class ComputerSettingResponse
{
[XmlElement("computer_setting")]
internal ComputerSetting Settings { get; set; }
}
internal class ComputerSetting
{
[XmlAttribute("id")]
internal string Id { get; set; }
[XmlAttribute("hospital_name")]
internal string HospitalName { get; set; }
[XmlAttribute("computer_type")]
internal string ComputerType { get; set; }
[XmlAttribute("environment")]
internal string Environment { get; set; }
[XmlAttribute("label_printer_name")]
internal string LabelPrinterName { get; set; }
[XmlAttribute("document_printer_name")]
internal string DocumentPrinterName { get; set; }
}
}
And this is my attempt to deserialize it
// Get ComputerSettings
String _Url = this.ApiUrl
+ "/api1/hospitals/foo/settings.xml"
+ "?access_token=" + Authentication.AccessToken;
XmlSerializer _Serializer = new XmlSerializer(typeof(ComputerSettingResponse));
ComputerSettingResponse _ComputerSettingResponse = new ComputerSettingResponse();
using (XmlTextReader _XmlReader = new XmlTextReader(_Url))
{
_ComputerSettingResponse = (ComputerSettingResponse)_Serializer.Deserialize(_XmlReader);
Debug.WriteLine(_ComputerSettingResponse.Settings.Environment);
}
But this throws NullReference exception when it gets to Debug.WriteLine()
{"Object reference not set to an instance of an object."}
I've checked that the url is returning the xml properly, so it must me a poorly constructed class, or I'm not doing the deserialization properly.
Upvotes: 2
Views: 1474
Reputation: 27904
Once XDocument came out ( matched with Linq ) , I stopped all the Xml Attribute voodoo. This "mapping" code is simple and straight forward.
public class ComputerSettingResponse
{
internal ComputerSetting Settings { get; set; }
}
internal class ComputerSetting
{
internal string Id { get; set; }
internal string HospitalName { get; set; }
internal string ComputerType { get; set; }
internal string Environment { get; set; }
internal string LabelPrinterName { get; set; }
internal string DocumentPrinterName { get; set; }
}
string xmlString = @"<response>
<computer_setting id=""1"" hospital_name=""foo"" computer_type=""bar"" environment=""staging"" label_printer_name=""labels"" document_printer_name=""docs""/>
</response> ";
XDocument xDoc = XDocument.Parse(xmlString);
//XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");
string ns = string.Empty;
List<ComputerSettingResponse> collection = new List<ComputerSettingResponse>
(
from list in xDoc.Descendants(ns + "response")
from item1 in list.Elements(ns + "computer_setting")
where item1 != null
select new ComputerSettingResponse
{
//note that the cast is simpler to write than the null check in your code
//http://msdn.microsoft.com/en-us/library/bb387049.aspx
Settings = new ComputerSetting
(
)
{
Id = (string)item1.Attribute("id") ?? string.Empty,
HospitalName = (string)item1.Attribute("hospital_name") ?? string.Empty,
ComputerType = (string)item1.Attribute("computer_type") ?? string.Empty,
Environment = (string)item1.Attribute("environment") ?? string.Empty,
LabelPrinterName = (string)item1.Attribute("label_printer_name") ?? string.Empty,
DocumentPrinterName = (string)item1.Attribute("document_printer_name") ?? string.Empty
}
}
);
/* if you know there is only one */
ComputerSettingResponse returnItem = collection.FirstOrDefault();
Upvotes: 1
Reputation: 3311
Visual Studio has some nice tooling to generate serializable classes from XML or JSON - just copy a sample of your XML to the clipboard, open an empty class file and paste it as classes with the following menu path in Visual Studio: Edit -> Paste Special -> Paste XML as Classes ...or, "Paste JSON as Classes" in the case of JSON.
Upvotes: 2
Reputation: 26223
Your properties are required to be public in order to be serialised. Per the documentation:
XML serialization is the process of converting an object's public properties and fields to a serial format (in this case, XML) for storage or transport.
Change Settings
from internal
to public
and it will deserialise correctly (you will also have to change the class modified on ComputerSetting
to public
in order to compile).
As an aside, you shouldn't be using XmlTextReader
, its use has been discouraged since .NET 2 was released. Per the remarks in the documentation, you should use XmlReader.Create
.
Upvotes: 1
Reputation: 6977
In the above code you seem to treat "_Url" as XML but it's just the URL of the backend API, isn't it?
I tried replacing it with your sample XML and made it work only by changing the class visibility to public
So this worked for me:
String xml = File.ReadAllText("XMLFile1.xml");
XmlSerializer _Serializer = new XmlSerializer(typeof(ComputerSettingResponse));
ComputerSettingResponse _ComputerSettingResponse = new ComputerSettingResponse();
using (StringReader reader = new StringReader(xml))
{
_ComputerSettingResponse = (ComputerSettingResponse)_Serializer.Deserialize(reader);
Debug.WriteLine(_ComputerSettingResponse.Settings.Environment);
}
using the class like this:
using System.Xml.Serialization;
namespace Casechek.Kiosk
{
[XmlRoot("response")]
public class ComputerSettingResponse
{
[XmlElement("computer_setting")]
public ComputerSetting Settings { get; set; }
}
public class ComputerSetting
{
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("hospital_name")]
public string HospitalName { get; set; }
[XmlAttribute("computer_type")]
public string ComputerType { get; set; }
[XmlAttribute("environment")]
public string Environment { get; set; }
[XmlAttribute("label_printer_name")]
public string LabelPrinterName { get; set; }
[XmlAttribute("document_printer_name")]
public string DocumentPrinterName { get; set; }
}
}
I looked into why it didn't work with internal declaration and found this SO answer which helped me understand a few things about the internals of XmlSerializer: https://stackoverflow.com/a/6156822/3093396
Hope this helps.
Upvotes: 1