RestSharp serialization not serializing class name correctly - c#

RestSharp doesn't seem to be paying attention to the "SerializeAs" attribute that I've decorated one of my class names with:
Person
[Serializable, SerializeAs(Name = "person")]
public class Person
{
[SerializeAs(Name = "first-name")]
public string FirstName { get; set; }
[SerializeAs(Name = "contact-data")]
public ContactData ContactData { get; set; }
}
ContactData
public class ContactData
{
[SerializeAs(Name = "email-addresses")]
public List<EmailAddress> EmailAddresses { get; set; }
}
EmailAddress
[SerializeAs(Name = "email-address")]
public class EmailAddress
{
[SerializeAs(Name = "address")]
public string Address { get; set; }
[SerializeAs(Name = "location")]
public string Location { get; set; }
}
I'm using the following code to serialize the XML:
var request = new RestRequest("people/{id}.xml", Method.PUT);
request.AddParameter("id", person.Id, ParameterType.UrlSegment);
request.XmlSerializer = new XmlSerializer();
request.AddBody(person);
However, the resulting XML looks like this:
<person>
<first-name>Scott</first-name>
<contact-data>
<email-adresses>
<EmailAddress>
<address>my#email.com</address>
<location>Work</location>
</EmailAddress>
</email-adresses>
</contact-data>
</person>
You can see that the <EmailAddress> element appears to be ignoring the SerializeAs attribute and is not serialized to "email-address" as I would expect it to be while all of the others work just fine. Does anyone know why this might be happening or how to fix it?

After poking around the RestSharp source, I realized this is a bug in the XmlSerializer built into RestSharp. I've fixed it and submitted a pull request.

Related

.net core deserialize xml response

I have this xml response as a stream from USPS 3.0 zip code look up api
<ZipCodeLookupResponse>
<Address ID="1">
<Address1>Unit 2</Address1>
<Address2>8 WILDWOOD DR</Address2>
<City>OLD LYME</City>
<State>CT</State>
<Zip5>06371</Zip5>
</Address>
<Address ID="2">
<Address1>Unit 2</Address1>
<Address2>8 WILDWOOD DR</Address2>
<City>OLD LYME</City>
<State>CT</State>
<Zip5>06371</Zip5>
</Address>
</ZipCodeLookupResponse>
and I'm trying to deserialize the response in an array of addresses.
[XmlRoot(ElementName = "ZipCodeLookupResponse")]
public class UspsAddress
{
[XmlArray("Address")]
public UspsAddressResult[] Addresses {get;set;}
}
[XmlRoot(ElementName = "Address")]
public class UspsAddressResult
{
[XmlElement(ElementName = "Address2")]
public string Adress1 { get; set; }
[XmlElement(ElementName = "Address1")]
public string Adress2 { get; set; }
[XmlElement(ElementName = "City")]
public string City { get; set; }
[XmlElement(ElementName = "State")]
public string State { get; set; }
[XmlElement(ElementName = "Zip5")]
public string Zip { get; set; }
}
This code is always returning an empty array. How can I fix this code to get both address from the response?
...
var content = await res.Content.ReadAsStreamAsync();
var serializer = new XmlSerializer(typeof(UspsAddress));
var results = (UspsAddress)serializer.Deserialize(content);
Instead of using XmlArray or XmlArrayItem, you can use the `XmlElement``
attribute to set the name of the child elements. The deserializer will recognize that there are multiple elements that are supposed to be deserialized into a list of objects.
Your class then looks like this:
[XmlRoot(ElementName = "ZipCodeLookupResponse")]
public class UspsAddress
{
[XmlElement("Address")]
public UspsAddressResult[] Addresses {get;set;}
}

create xml from object without setting values in it c#

I want to convert a c# class to an xml file without declaring default values in it. If I declare values on every propery in the class I got it to work and the XML has all the properties in it. The primarydata is my class with properties in it.
var pD = new PrimaryData();
XmlSerializer serializerPrimaryData = new XmlSerializer(typeof(PrimaryData));
serializerPrimaryData.Serialize(File.Create(xmlLocation), pD,ns);
But I dont want to declare any values.
If i run this code I get just:
<?xml version="1.0"?>
<PrimaryData />
I don't get the properties in the class as you can see. So how can I get the properties in the class without declaring them to a default value?
Any suggestions?
I have followed this guide: https://codehandbook.org/c-object-xml/
But he is declaring default values to his class.
public class PrimaryData
{
public PrimaryData();
public string BatchId { get; set; }
public CurrentOperation CurrentOperation { get; set; }
public Heat Heat { get; set; }
public string MaterialId { get; set; }
public List<Operation> Operations { get; set; }
public OrderInfo OrderInfo { get; set; }
public Plate Plate { get; set; }
}
Just set default values.
public class Employee
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
}
public static void Main(string[] args)
{
var employee = new Employee();
var sw = new StringWriter();
var se = new XmlSerializer(employee.GetType());
var tw = new XmlTextWriter(sw);
se.Serialize(tw, employee);
Console.WriteLine(sw.ToString());
Console.Read();
}
Result
<?xml version="1.0" encoding="utf-16"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName />
<LastName />
</Employee>
Second solution is to set XmlElement(IsNullable = true)
public class Employee
{
[XmlElement(IsNullable = true)]
public string FirstName { get; set; }
[XmlElement(IsNullable = true)]
public string LastName { get; set; }
}
I have no clue what you are trying to do ...
XML serialization just makes a mirror of the object in different structure (aka XML - object will be represented by structured text).
If your class has no properties, nothing will be serialized to XML but only the base empty object.
My suggestion is to make a class with Dictionary<string, object> as property. Then you will be able to write into the Dictionary and serialize it.
But, it has some drawbacks. XML Serialization (if I remember correctly) does not support Dictionaries, so I would go with DataContract instead.
That might work :)
[DataContract]
public class PrimaryData
{
[DataMember(Name = "Data", Order = 0, IsRequired = true)]
public Dictionary<string, object> Data { get; set; }
}
You should add this annotation to your properties:
[XmlElement(IsNullable = true)]
public string Prop { get; set; }
The result in your xml should be something like this:
<Prop xsi:nil="true" />

Restsharp xml Deserialization to list without changing the name of model

I have xml that is not very well formed, but need to map to a List with RestSharp. I do not have control of the service/ xml output. Thus far, I was able to get around issues with the properties themselves using the DeserializeAs(Name="name")] property. For instance,
public class Physician
{
[DeserializeAs(Name = "personId")]
public string Id { get; set; }
[DeserializeAs(Name = "fName")]
public string FirstName { get; set; }
[DeserializeAs(Name = "lName")]
public string LastName { get; set; }
}
Maps correctly to a list when I have the following xml:
<data>
<physician>
<personId>3325</personId>
<fName>Foo</fName>
<lName>Bar</lName>
</physician>
<physician>
<personId>3342</personId>
<fName>Jane</fName>
<lName>Doe</lName>
</physician>
...
</data>
The function I am using is:
public static List<T> GetListOfEntityType<T>(string url)
{
return Client.Execute<List<T>>(new RestRequest(url)).Data;
}
The problem is that I have xml that looks like this for a number of other requests,
<data>
<row>
<typeId>0</typeId>
<type>Physician</type>
</row>
<row>
<typeId>1</typeId>
<type>Ambulance</type>
</row>
...
</data>
Given it is not very descriptive xml, but I need to map this to a List.
public class OrganizationType
{
public string typeId { get; set; }
public string type { get; set; }
}
https://stackoverflow.com/a/4082046/3443716 somewhat answers this, and it certainly works, but I do not want the model to be named row I tried to do this:
[DeserializeAs(Name = "row")]
public class OrganizationType
{
public string typeId { get; set; }
public string type { get; set; }
}
However RestSharp appers to ignore this attribute entirely. I have been searching a ton and found a few answers that suggest using a custom deserializer, but I have a hard time believing that is the only or easiest option for that matter. Is there some other attribute that I may be missing or is the only option using a custom deserializer?
As another note, I also tried to do something like this and I just get null back....
public class OrganizationType
{
public string typeId { get; set; }
public string type { get; set; }
}
public class OrgTypeCollection
{
[DeserializeAs(Name = "row")]
public List<OrganizationType> Names { get; set; }
}
Thanks to this post, https://stackoverflow.com/a/27643726 I was able to "fork" the RestSharp Deserialzier and create a slightly custom one with the two line modification provided by The Muffin Man as follows
Added this to HandleListDerivative in the RestSharp.Deserializers.XmlDeserializer at line 344.
var attribute = t.GetAttribute<DeserializeAsAttribute>();
if (attribute != null) name = attribute.Name;
That allowed me to as desired add DeserializeAs as follows:
[DeserializeAs(Name = "row")]
public class OrganizationType
{
public string typeId { get; set; }
public string type { get; set; }
}
I am unsure why this is ignored by restsharp, this seems like it would be useful in a number of cases... As a side note, the functionality of creating nested lists is still available as well. Though I haven't run the tests after modification, it appears to do exactly what you would expect. Other than that all you have to do is add the custom handler to rest by callling
Client.AddHandler("application/xml", new CustomXmlDeserializer());

Removing XML namespace from WebApi

I'm working with a WebApi and XML as result.
I just keep receiving a weird namespace in my result, for exemple:
<QueryId>FE04A4E6-A584-47BF-9DA1-7360DFB08A8D</QueryId>
<ExecutionError>true</ExecutionError>
<OperationResult xmlns:d2p1="http://schemas.datacontract.org/2004/07/MyTypes" i:nil="true"/>
<ErrorMessage>INVALID ACCOUNT</ErrorMessage>
I read some solutions and included this at my WebApiConfig.cs:
config.Formatters.XmlFormatter.UseXmlSerializer = true;
But using this parameter, my return was the same without OperationResult tag (the only one that was not filled out):
<QueryId>FE04A4E6-A584-47BF-9DA1-7360DFB08A8D</QueryId>
<ExecutionError>true</ExecutionError>
<ErrorMessage>INVALID ACCOUNT</ErrorMessage>
I was trying to use my result object like this:
[DataContract(Namespace = "")]
public class CustomRecordResult
{
[DataMember]
public string QueryId { get; set; }
[DataMember]
public bool ExecutionError { get; set; }
[DataMember]
public string ErrorMessage { get; set; }
[DataMember]
public CustomSourceRecord OperationResult { get; set; }
}
But it´s not working. My result object is empty in this case
Any ideas?
Thank you very much!
Use the EmitDefaultValue option to control whether that member will be serialized when it is null.
[DataMember(EmitDefaultValue = false)]
public CustomSourceRecord OperationResult { get; set; }

Deserializing child elements in xml

How should I get the <site-standard-profile-request> child element to deserialize correctly so that it does not show up as null?
The deserialization process is perfect; I just need to get the child element <site-standard-profile-request> to serialize as well.
//<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
//- <person>
// <first-name>Storefront</first-name>
// <last-name>Doors</last-name>
// <headline>CEO at StorefrontDoors.NET</headline>
//- <site-standard-profile-request>
// <url>http://www.linkedin.com/profile?viewProfile=&key=147482099&authToken=-Igm&authType=name&trk=api*a216630*s224617*</url>
// </site-standard-profile-request>
// </person>
[XmlRoot("person")]
[Serializable()]
public class LinkedIn
{
[XmlElement("first-name")]
public string FirstName { get; set; }
[XmlElement("last-name")]
public string LastName { get; set; }
[XmlElement("headline")]
public string Headline { get; set; }
public string URL { get; set; }
}
string profile = oauth.APIWebRequest("GET", "https://api.linkedin.com/v1/people/~", null);
//
LinkedIn lkIn = null;
BufferedStream stream = new BufferedStream(new MemoryStream());
stream.Write(Encoding.ASCII.GetBytes(profile), 0, profile.Length);
stream.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(stream);
XmlSerializer serializer = new XmlSerializer(typeof(LinkedIn));
lkIn = (LinkedIn)serializer.Deserialize(sr);
stream.Close();
You'll need another serializable class with just the url as a property. Eg,
[XmlRoot("site-standard-profile-request")]
[Serializable()]
public class StandardProfile
{
public string url { get;set;}
}
And then your existing class should use it, something like
[XmlRoot("person")]
[Serializable()]
public class LinkedIn
{
[XmlElement("first-name")]
public string FirstName { get; set; }
[XmlElement("last-name")]
public string LastName { get; set; }
[XmlElement("headline")]
public string Headline { get; set; }
public StandardProfile Profile { get;set; }
}
I haven't tested this code, but should be pretty close.
Hope that helps.

Categories