I am trying to deserialize an XDocument to a class. I am calling USPS the CityStateLookupResponse API. Every time I deserialize to my class object, the object is always null.
Here is my class
[Serializable()]
[XmlRoot("CityStateLookupResponse", IsNullable = false)]
public class CityStateLookUpResponse
{
[XmlAttribute("ID")]
public string Id { get; set; }
[XmlElement("City")]
public string City { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("Zip5")]
public string Zip { get; set; }
}
Below is the code I use to call USPS
XDocument requestDoc = new XDocument(
new XElement("CityStateLookupRequest",
new XAttribute("USERID", _postOfficeOptions.UserId),
new XElement("Revision", "1"),
new XElement("ZipCode",
new XAttribute("ID", "0"),
new XElement("Zip5", zipCode.ToString())
)
)
);
try
{
var url = _postOfficeOptions.Url + requestDoc;
var client = new WebClient();
var response = client.DownloadString(url);
var xdoc = XDocument.Parse(response.ToString());
XmlSerializer serializer = new XmlSerializer(typeof(CityStateLookUpResponse));
if (!xdoc.Descendants("ZipCode").Any()) return null;
return (CityStateLookUpResponse)serializer.Deserialize(xdoc.CreateReader());
}
catch (Exception ex)
{
throw new Exception("In CityStateLookUp:", ex);
}
This line of code always returns null
return (CityStateLookUpResponse)serializer.Deserialize(xdoc.CreateReader());
This is a valid response from the USPS API
<?xml version="1.0"?>
<CityStateLookupResponse><ZipCode ID="0"><Zip5>90210</Zip5>
<City>BEVERLY HILLS</City><State>CA</State></ZipCode>
</CityStateLookupResponse>
Any help would be appreciated
The problem is that you are trying to deserialize starting at the wrong node. The root node for your response is CityStateLookupResponse. That contains a list of ZipCode nodes, and it is the ZipCode nodes that correspond to your current CityStateLookUpResponse class.
You can fix this by changing your response class like this:
[Serializable()]
[XmlRoot("CityStateLookupResponse", IsNullable = false)]
public class CityStateLookupResponse
{
[XmlElement("ZipCode")]
public List<ZipCode> ZipCode { get; } = new();
}
[Serializable()]
[XmlRoot("ZipCode", IsNullable = false)]
public class ZipCode
{
[XmlAttribute("ID")]
public string Id { get; set; }
[XmlElement("City")]
public string City { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("Zip5")]
public string Zip { get; set; }
}
Related
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>
I am trying to deserialize a string to object. Is xml node like syntax, but is not an xml (as there is no root node or namespace). This is what I have so far, having this error:
<delivery xmlns=''>. was not expected
Deserialize code:
var number = 2;
var amount = 3;
var xmlCommand = $"<delivery number=\"{number}\" amount=\"{amount}\" />";
XmlSerializer serializer = new XmlSerializer(typeof(Delivery));
var rdr = new StringReader(xmlCommand);
Delivery delivery = (Delivery)serializer.Deserialize(rdr);
Delivery object:
using System.Xml.Serialization;
namespace SOMWClient.Events
{
public class Delivery
{
[XmlAttribute(AttributeName = "number")]
public int Number { get; set; }
[XmlAttribute(AttributeName = "amount")]
public string Amount { get; set; }
public Delivery()
{
}
}
}
How can I avoid the xmlns error when deserializing ?
Change the Delivery class and add information about the root element (XmlRoot attribute):
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[XmlRoot("delivery")]
public class Delivery
{
[XmlAttribute(AttributeName = "number")]
public int Number { get; set; }
[XmlAttribute(AttributeName = "amount")]
public string Amount { get; set; }
public Delivery()
{ }
}
Add the root yourself like this:
XmlRootAttribute root = new XmlRootAttribute();
root.ElementName = "delivery";
// root.Namespace = "http://www.whatever.com";
root.IsNullable = true;
// your code goes below
I am looking to deserialize data and place it into a generic class from a response from Azure.
<ServiceResources xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windowsazure">
<ServiceResource>
<Name>Airport1</Name>
<Type>Microsoft.SqlAzure.FirewallRule</Type>
<State>Normal</State>
<SelfLink>https://management.core.windows.net:xxx/xxx/services/sqlservers/servers/xxx/firewallrules/Airport1</SelfLink>
<ParentLink>https://management.core.windows.net:xxxx/services/sqlservers/servers/xxx</ParentLink>
<StartIPAddress>000.000.000.000</StartIPAddress>
<EndIPAddress>2000.000.000.000</EndIPAddress>
</ServiceResource>
There are several objects I need to deserialze into my class.
[Serializable, XmlRoot(ElementName = "ServiceResource", Namespace = "http://schemas.microsoft.com/windowsazure/")]
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
I have tried several different ventures into this and can't nail it. I have used the xmlSerializer but hit blocks on that.
using (var responseStreamReader = new StreamReader(webResponse.GetResponseStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceResource));
ServiceResource deserialized = (ServiceResource)serializer.Deserialize(responseStreamReader);
}
Any help would be gratefully accepted.
Answer
The Azure REST Api is returning a list of ServiceResource in the XML. So you need to encapsulate that into a class. Here is an example.
[XmlRoot(
ElementName = "ServiceResources",
Namespace = "http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
public ServiceResources()
{
Items = new List<ServiceResource>();
}
[XmlElement("ServiceResource")]
public List<ServiceResource> Items { get; set; }
}
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
With those two classes, you can now do the following.
var response = request.GetResponse();
var message = string.Empty;
using (var responseStreamReader = new StreamReader(response.GetResponseStream()))
{
message = responseStreamReader.ReadToEnd();
}
var textReader = new StringReader(message);
var serializer = new XmlSerializer(typeof(ServiceResources));
var serviceResources =
serializer.Deserialize(textReader) as ServiceResources;
Demo Console App
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace DeserializeAzureXmlResponse
{
class Program
{
private static string certificateThumbprint = "19DAED4D4ABBE0D400DC33A6D99D00D7BBB24472";
private static string subscriptionId = "14929cfc-3501-48cf-a5c9-b24a7daaf694";
static string sqlServerName = "mvp2015";
static string managementUri = "https://management.core.windows.net";
static string sqlServerApi = "services/sqlservers/servers";
static string firewallRules = "firewallrules";
static void Main(string[] args)
{
var restUri = CreateRestUri();
var clientCert = GetX509FromPersonalStore();
var request = (HttpWebRequest)HttpWebRequest.Create(restUri);
request.Headers.Add("x-ms-version", "2012-03-01");
request.ClientCertificates.Add(clientCert);
var response = request.GetResponse();
var message = string.Empty;
using (var responseStreamReader = new StreamReader(response.GetResponseStream()))
{
message = responseStreamReader.ReadToEnd();
}
var textReader = new StringReader(message);
var serializer = new XmlSerializer(typeof(ServiceResources));
var serviceResources = serializer.Deserialize(textReader) as ServiceResources;
foreach (var sr in serviceResources.Items)
{
Console.WriteLine("Name".PadRight(20) + sr.Name);
Console.WriteLine("Type".PadRight(20) + sr.Type);
Console.WriteLine("State".PadRight(20) + sr.State);
Console.WriteLine("SelfLink".PadRight(20) + sr.SelfLink);
Console.WriteLine("ParentLink".PadRight(20) + sr.ParentLink);
Console.WriteLine("StartIP".PadRight(20) + sr.StartIPAddress);
Console.WriteLine("EndIP".PadRight(20) + sr.EndIPAddress);
Console.WriteLine("+++++++++++");
}
Console.ReadLine();
}
static Uri CreateRestUri()
{
// https://management.core.windows.net/{subscriptionID}/services/sqlservers/servers/{server}/firewallrules/
var builder = new StringBuilder();
builder.Append(managementUri + "/");
builder.Append(subscriptionId + "/");
builder.Append(sqlServerApi + "/");
builder.Append(sqlServerName + "/");
builder.Append(firewallRules + "/");
var uri = new Uri(builder.ToString());
return uri;
}
static X509Certificate GetX509FromPersonalStore()
{
// To view the personal store, press `Win + R` and then type `certmgr.msc`
var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, true);
var certificate = certificates[0];
store.Close();
return certificate;
}
}
[XmlRoot(
ElementName = "ServiceResources",
Namespace = "http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
public ServiceResources()
{
Items = new List<ServiceResource>();
}
[XmlElement("ServiceResource")]
public List<ServiceResource> Items { get; set; }
}
public class ServiceResource
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Type")]
public string Type { get; set; }
[XmlElement("State")]
public string State { get; set; }
[XmlElement("SelfLink")]
public string SelfLink { get; set; }
[XmlElement("ParentLink")]
public string ParentLink { get; set; }
[XmlElement("StartIPAddress")]
public string StartIPAddress { get; set; }
[XmlElement("EndIPAddress")]
public string EndIPAddress { get; set; }
}
}
Output
Name My-House
Type Microsoft.SqlAzure.FirewallRule
State Normal
SelfLink https://management.core.windows.net/14929cfc-35
ParentLink https://management.core.windows.net/14929cfc-35
StartIP 123.435.234.643
EndIP 123.435.234.643
+++++++++++
Name AllowAllWindowsAzureIps
Type Microsoft.SqlAzure.FirewallRule
State Normal
SelfLink https://management.core.windows.net/14929cfc-35
ParentLink https://management.core.windows.net/14929cfc-35
StartIP 0.0.0.0
EndIP 0.0.0.0
+++++++++++
See Also
Is it possible to deserialize XML into List<T>?
List Firewall Rules
I am assuming you are trying to deserialize the whole object graph. Given xml has root node ServiceResources which contains ServiceResource. You have two options, you can mimic the whole xml into classes and desrialize; or just get the inner node of ServiceResource and deserialize that part.
If you use first option, then you would need to store ServiceResource inside another class which has mapped collections property with XmlElement name set to "ServiceResource", e.g.:
[XmlType(Namespace="http://schemas.microsoft.com/windowsazure")]
[XmlRoot(Namespace="http://schemas.microsoft.com/windowsazure")]
public class ServiceResource
{
public string Name { get; set; }
public string Type { get; set; }
public string State { get; set; }
public string SelfLink { get; set; }
public string ParentLink { get; set; }
public string StartIPAddress { get; set; }
public string EndIPAddress { get; set; }
}
[XmlType(Namespace="http://schemas.microsoft.com/windowsazure")]
[XmlRoot(Namespace="http://schemas.microsoft.com/windowsazure")]
public class ServiceResources
{
[XmlElement("ServiceResource")]
public List<ServiceResource> ServiceResource { get; set; }
}
With that you should be able to deserialize directly. Container class has the collections mapped to the ServiceResource element, which will load all of the nodes for service resource. Note: deserialization target type is now "ServiceResources" not the inner type "ServiceResource"
using (var responseStreamReader = new StreamReader(webResponse.GetResponseStream()))
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceResources));
ServiceResources deserialized = (ServiceResources)serializer.Deserialize(responseStreamReader);
//you can access each item in loop
foreach(var res in deserialized.ServiceResource)
{
//access items e.g.
var name = res.Name;
}
}
Im trying to deserialize an xml response from from Amazons simple db service.
The xml is like this.
<ListDomainsResponse xmlns="http://sdb.amazonaws.com/doc/2009-04-15/">
<ListDomainsResult>
<DomainName>Logging</DomainName>
<DomainName>teets</DomainName>
</ListDomainsResult>
<ResponseMetadata>
<RequestId>9d48122e-1ddf-8771-b771-b36599838ea0</RequestId>
<BoxUsage>0.0000071759</BoxUsage>
</ResponseMetadata>
</ListDomainsResponse>
And the object i'm trying to serialize into is this.
public class ListDomainsResponse : Response
{
public ListDomainsResult ListDomainsResult { get; set; }
}
public class ListDomainsResult
{
public List<String> DomainNames { get; set; }
public string NextToken { get; set; }
}
public class Response
{
public static string NameSpace = "http://sdb.amazonaws.com/doc/2009-04-15/";
public ResponseMetadata ResponseMetadata{ get; set; }
}
public class ResponseMetadata
{
public string RequestId { get ; set; }
public string BoxUsage { get; set; }
}
My problem is that the response metadata is getting deserialized correctly but the List of strings called DomainName is not being deserialized, it is empty each time.
Can someone see what i'm doing wrong.
Updated with more code
public async Task<Response> ListDomains (ListDomainsRequest request)
{
using (Client = new HttpClient ()) {
ListDomainsRequestMarshaller marshaller = new ListDomainsRequestMarshaller ();
marshaller.Configure (request);
HttpResponseMessage responseMessage = Client.GetAsync (marshaller.Marshal ()).Result;
ListDomainsResponseUnMarshaller unmarshaler = new ListDomainsResponseUnMarshaller (responseMessage);
return unmarshaler.Response;
}
}
public ListDomainsResponseUnMarshaller (HttpResponseMessage message)
{
XDocument doc = XDocument.Load (message.Content.ReadAsStreamAsync ().Result);
if (message.StatusCode.Equals (HttpStatusCode.OK)) {
Serializer = new XmlSerializer (typeof(ListDomainsResponse), Response.NameSpace);
Response = (ListDomainsResponse)Serializer.Deserialize (doc.CreateReader ());
Response.HttpStatusCode = message.StatusCode;
Response.ContentLength = (long)message.Content.Headers.ContentLength;
} else {
Serializer = new XmlSerializer (typeof(Response));
Response = (Response)Serializer.Deserialize (doc.CreateReader ());
Response.HttpStatusCode = message.StatusCode;
Response.ContentLength = (long)message.Content.Headers.ContentLength;
}
Serializer = null;
}
Adding Attributes helped
[System.Xml.Serialization.XmlTypeAttribute (Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
[System.Xml.Serialization.XmlRootAttribute ("ListDomainsResponse", Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
public class ListDomainsResponse : Response
{
public ListDomainsResult ListDomainsResult { get; set; }
}
[System.Xml.Serialization.XmlTypeAttribute (Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
[System.Xml.Serialization.XmlRootAttribute (Namespace = "http://sdb.amazonaws.com/doc/2009-04-15/")]
public class ListDomainsResult
{
[System.Xml.Serialization.XmlElementAttribute ("DomainName")]
public string[] DomainName { get; set; }
public string NextToken { get; set; }
}
Hoping to get some help on this: I am very thankful that a developer shared this XML file with me because it should save me a lot of headache. But he told me I am on my own with how to read it. Basically I am writing a windows store app for a card game. I have a XML that is my list of cards and want to read it into a list. I get no errors, and have read XML into lists before. Any advice would be appreciated.
Here is a snippet of XML:
<?xml version="1.0" encoding="UTF-8"?><carddatabase>
<cards>
<card>
<name>Tundra Kavu</name>
<set picURL="http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=26805&type=card" picURLHq="" picURLSt="">AP</set>
<color>R</color>
<manacost>2R</manacost>
<type>Creature - Kavu</type>
<pt>2/2</pt>
<tablerow>2</tablerow>
<text>{T}: Target land becomes a Plains or an Island until end of turn.</text>
</card>
</cards>
</carddatabase>
Here is my serializer:
public async void readFile()
{
StorageFolder myFolder = ApplicationData.Current.LocalFolder;
StorageFile myFile = await myFolder.CreateFileAsync("cards.xml", CreationCollisionOption.OpenIfExists);
XmlSerializer Serializer = new XmlSerializer(typeof(List<card>), new XmlRootAttribute("carddatabase"));
string XmlString = await FileIO.ReadTextAsync(myFile);
XmlDocument xmlDoc = await XmlDocument.LoadFromFileAsync(myFile);
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Auto, IgnoreWhitespace = true, IgnoreComments = true };
var stringReader = new StringReader(XmlString);
XmlReader reader = XmlReader.Create(stringReader, settings);
List<card> temp = (List<card>)Serializer.Deserialize(reader);
foreach(card x in temp)
{
await tempTable.InsertAsync(x);
}
}
Here is my card class:
public class card
{
public string id { get; set; }
public string name { get; set; }
public string manacost { get; set; }
public string set { get; set; }
public string color { get; set; }
public string tablerow { get; set; }
public string text { get; set; }
public string type { get; set; }
}
You can parse xml with Linq:
XDocument xdoc = XDocument.Load(myFile);
var cards = from c in xdoc.Descendants("card")
select new card() {
name = (string)c.Element("name"),
manacost = (string)c.Element("manacost"),
set = (string)c.Element("set"),
color = (string)c.Element("color"),
tableRow = (string)c.Element("tablerow"),
text = (string)c.Element("text"),
type = (string)c.Element("type")
};
foreach(var card in cards)
await tempTable.InsertAsync(card);
Also Linq allows you to cast values from string to other datatypes, so you can have property int TableRow { get; set; } in your class, which could be parsed as TableRow = (int)c.Element("tablerow") (or int? if tablerow element is not required).
BTW in C# we use CamelCase names for types and properties. So, consider to have type like:
public class Card
{
public string Id { get; set; }
public string Name { get; set; }
public string ManaCost { get; set; }
public string Set { get; set; }
public string Color { get; set; }
public int TableRow { get; set; }
public string Text { get; set; }
public string Type { get; set; }
}