2

I'm having trouble deserializing an XML response from an API call. My 'Option' object's property 'Description' is null.

Below is a sample of the XML:

<vehicle found="1">
   <description>VehicleDescText</description>
   <buildDate>2000-11-20</buildDate>
   <modelYear>2001</modelYear>
   <optionList>
      <option code="UH8">OptionDesc1</option>
      <option code="UH8">OptionDesc2</option>
   </optionList>
</vehicle>

Here is a sample of C# classes:

[DataContract]
[XmlRoot("vehicle")]
public class Vehicle
{
    [DataMember]
    [XmlAttribute("found")]
    public bool Found { get; set; }

    [DataMember]
    [XmlElement("description")]
    public string Description { get; set; }

    [DataMember]
    [XmlElement("buildDate")]
    public string BuildDate { get; set; }

    [DataMember]
    [XmlElement("modelYear")]
    public string ModelYear { get; set; }

    [DataMember]
    [XmlElement("optionList")]
    public List<Option> OptionList { get; set; }
}

public class Option
{
    [DataMember]
    [XmlAttribute("code")]
    public string Code { get; set; }

    [DataMember]
    [XmlElement("option")]
    public string Description { get; set; }
}

Deserializing the object looks like this:

var xmlDeserializer = new RestSharp.Deserializers.XmlDeserializer();
results = xmlDeserializer.Deserialize<Vehicle>(response);

Where am I going wrong here? As I would prefer not to modify the underlying data model, what Attributes can I add or modify to fix the problem?

dbc
  • 104,963
  • 20
  • 228
  • 340
TopBanana9000
  • 818
  • 2
  • 14
  • 32
  • 3
    The XML sample you provided has Vehicle for root, not ResponseResult. Shouldn't you be calling Deserialize instead? – Gabriel Rainha Oct 30 '17 at 20:41
  • @GabrielRainha I cannot create a wrapper class for the Vehicle? – TopBanana9000 Oct 30 '17 at 20:42
  • If your response is not wrapping a Vehicle, no. Is that sample XML an exact copy of your response object? – Gabriel Rainha Oct 30 '17 at 20:43
  • You should be deserializing the vehicle. – Rogala Oct 30 '17 at 20:45
  • It is an abridged response. Identical in structure, though. I moved the XmlRoot attribute to my ResponseResult declaration. Still a null object on serialization. Going to remove the wrapper and see what happens.. I don't NEED it – TopBanana9000 Oct 30 '17 at 20:45
  • I have edited the code to what I am working with now. The only thing coming back as null at the moment is the Description property on Option – TopBanana9000 Oct 30 '17 at 20:49
  • 1
    What is `XmlDeserializer`? Does it use `XmlSerializer` or `DataContractSerializer`? Can you provide a [mcve] we can compile, run and test? – dbc Oct 30 '17 at 20:58
  • @dbc RestSharp.Deserializers is where XmlSerializer comes from – TopBanana9000 Oct 30 '17 at 20:59
  • 2
    `RestSharp.Deserializers.XmlDeserializer` doesn't use data contract attributes or `XmlSerializer` attributes. It has its own. See https://github.com/restsharp/RestSharp/wiki/Deserialization. Maybe you want to use [`DotNetXmlDeserializer`](https://github.com/restsharp/RestSharp/blob/master/RestSharp/Deserializers/DotNetXmlDeserializer.cs) instead? – dbc Oct 30 '17 at 21:08
  • 1
    Could you try use [XmlText] instead of [XmlElement("option")]? See https://stackoverflow.com/questions/14245846/deserializing-xml-file-with-multiple-element-attributes-attributes-are-not-des – rudolf_franek Oct 30 '17 at 21:08
  • You need to rename `Description` to `Value` as explained in [How to deserialize element with attribute](https://stackoverflow.com/a/12227365/3744182). In fact I think this is a duplicate. – dbc Oct 30 '17 at 23:18
  • @dbc Datacontract attributes are for something else. I'll give this a shot if I cannot get this going without changing property names. – TopBanana9000 Oct 31 '17 at 00:22
  • @rudolf_franek I will certainly try this tomorrow. Thank you – TopBanana9000 Oct 31 '17 at 00:22

3 Answers3

7

You have marked your types with XML serializer attributes and data contract attributes, but the deserializer you are using, RestSharp.Deserializers.XmlDeserializer, does not support these attributes.

Instead, as explained in its documentation, it supports the [DeserializeAs] attribute which allows for control of XML node name and element vs. attribute status.

But as noted in @apocalypse's answer as well as this older question there is a special case in the documentation regarding deserialization of element values into property values:

If the XML returned is like this:

<Response>Hello world</Response>

There's no way to directly represent that in a C# class:

public class Response {
}

You need something to hold the value of the Response element. In this case, add a property called Value and it will be populated:

public class Response {
     public string Value { get; set; }
}

This condition is checked for between searching for matching element names and matching attribute names.

I.e. if your rename Description to Value you will be able to deserialize that XML successfully. (Sample fiddle #1.)

However, it appears you do not wish to rename your Description property. If so, you can instead apply [DeserializeAs(Name = "Value")] to it and the special case will again apply:

public class Option
{
    public string Code { get; set; }

    [DeserializeAs(Name = "Value")]
    public string Description { get; set; }
}

Sample fiddle #2.

Finally, as an alternative solution you could switch to RestSharp.Deserializers.DotNetXmlDeserializer and use regular XmlSerializer attributes, specifically [XmlText]. Thus your code would become:

var xmlDeserializer = new RestSharp.Deserializers.DotNetXmlDeserializer();
var results = xmlDeserializer.Deserialize<Vehicle>(response);

And your types would look like:

[XmlRoot("vehicle")]
public class Vehicle
{
    [XmlAttribute("found")]
    public bool Found { get; set; }

    [XmlElement("description")]
    public string Description { get; set; }

    [XmlElement("buildDate")]
    public string BuildDate { get; set; }

    [XmlElement("modelYear")]
    public string ModelYear { get; set; }

    [XmlArray("optionList")]
    [XmlArrayItem("option")]
    public List<Option> OptionList { get; set; }
}

public class Option
{
    [XmlAttribute("code")]
    public string Code { get; set; }

    [XmlText]
    public string Description { get; set; }
}

Sample fiddle #3.

dbc
  • 104,963
  • 20
  • 228
  • 340
2

Read docs: https://github.com/restsharp/RestSharp/wiki/Deserialization

Change Description property to Value property and it will work (in Option class).

Can't give you better answer because I have never used RestSharp :)

apocalypse
  • 5,764
  • 9
  • 47
  • 95
1

In the XML, the value you want isn't an inner XmlElement - it's the current XmlElement's value.

You could mark the Option class' Description property with XmlText instead of XmlElement.

See related answer.

Danny Varod
  • 17,324
  • 5
  • 69
  • 111