Teddy Higgins Teddy Higgins - 21 days ago 4
ASP.NET (C#) Question

XML - Deserialize into object

I am trying to deserialize a API response into an object but the object keeps returning NULL. I can't figure it out. I feel like I'm missing something minor.

Here the c# deserialize code.

var request = (HttpWebRequest)HttpWebRequest.Create(uri + data);
var response = (HttpWebResponse)request.GetResponse();
var reader = new StreamReader(response.GetResponseStream());

PICKUPREQUESTRESPONSE PICKUPREQUESTRESPONSE = null;

XmlSerializer serializer = new XmlSerializer(typeof(PICKUPREQUESTRESPONSE));


PICKUPREQUESTRESPONSE = (PICKUPREQUESTRESPONSE)serializer.Deserialize(reader);
reader.Close();


this is the XML response from the API.

<PICKUPREQUESTRESPONSE xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<STATUS>
<CODE>1</CODE>
<VIEW>ERROR</VIEW>
<ERRORTYPE>INPUT_ERROR</ERRORTYPE>
<MESSAGE>Testing Mode: You have passed input validation, the pickup request has NOT been submitted</MESSAGE>
<VERSION>
<CURRENT>V3X1</CURRENT>
<CURRENT_RELEASE_DATE>03/01/2014</CURRENT_RELEASE_DATE>
<LATEST>V3X1</LATEST>
<LATEST_RELEASE_DATE>03/01/2014</LATEST_RELEASE_DATE>
</VERSION>
</STATUS>
</PICKUPREQUESTRESPONSE>


here is the c# object I've created.

[Serializable()]
[System.Xml.Serialization.XmlRoot("PICKUPREQUESTRESPONSE")]
public class PICKUPREQUESTRESPONSE
{
[XmlArray("PICKUPREQUESTRESPONSE")]
[XmlArrayItem("STATUS", typeof(STATUS))]
public STATUS[] STATUS { get; set; }
}


[Serializable()]
public class STATUS
{
[XmlElement("CODE")]
public string CODE { get; set; }

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

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

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

[XmlArrayItem("VERSION", typeof(VERSION))]
public VERSION[] VERSION { get; set; }

}

[Serializable()]
public class VERSION
{
[System.Xml.Serialization.XmlElement("CURRENT")]
public string CURRENT { get; set; }

[System.Xml.Serialization.XmlElement("CURRENT_RELEASE_DATE")]
public string CURRENT_RELEASE_DATE { get; set; }

[System.Xml.Serialization.XmlElement("LATEST")]
public string LATEST { get; set; }

[System.Xml.Serialization.XmlElement("LATEST_RELEASE_DATE")]
public string LATEST_RELEASE_DATE { get; set; }

}

Answer

Your Data Contracts are constructed in the wrong way

public class PICKUPREQUESTRESPONSE
{
    [XmlArray("PICKUPREQUESTRESPONSE")]
    [XmlArrayItem("STATUS", typeof(STATUS))]
    public STATUS[] STATUS { get; set; }
}

This would assume your xml looks like this

<?xml version="1.0" encoding="utf-16"?>
<PICKUPREQUESTRESPONSE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <PICKUPREQUESTRESPONSE>
    ...

The same goes for VERSION. By specifying XmlArray and XmlArrayItem it will expect nested instances of the elements which your sample xml didn't have.

The objects below should work

[Serializable()]
[System.Xml.Serialization.XmlRoot("PICKUPREQUESTRESPONSE")]
public class PICKUPREQUESTRESPONSE
{
    //[XmlArray("PICKUPREQUESTRESPONSE")]
    //[XmlArrayItem("STATUS", typeof(STATUS))]
    [XmlElement("STATUS")]
    public STATUS[] STATUS { get; set; }
}


[Serializable()]
public class STATUS
{
    [XmlElement("CODE")]
    public string CODE { get; set; }

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

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

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

    //[XmlArrayItem("VERSION", typeof(VERSION))]
    [XmlElement("VERSION")]
    public VERSION[] VERSION { get; set; }

}

[Serializable()]
public class VERSION
{
    [System.Xml.Serialization.XmlElement("CURRENT")]
    public string CURRENT { get; set; }

    [System.Xml.Serialization.XmlElement("CURRENT_RELEASE_DATE")]
    public string CURRENT_RELEASE_DATE { get; set; }

    [System.Xml.Serialization.XmlElement("LATEST")]
    public string LATEST { get; set; }

    [System.Xml.Serialization.XmlElement("LATEST_RELEASE_DATE")]
    public string LATEST_RELEASE_DATE { get; set; }

}

I also recommend using a standard pattern for xml serialization, something like this

/// <summary>
/// Deserialize an xml string to type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="xml"></param>
/// <returns></returns>
public static T Deserialize<T>(string xml)
{
    if (string.IsNullOrEmpty(xml))
        return default(T);

    XmlSerializer serializer = new XmlSerializer(typeof(T));

    XmlReaderSettings settings = new XmlReaderSettings();
    // No settings need modifying here

    using (StringReader textReader = new StringReader(xml))
    {
        using (XmlReader xmlReader = XmlReader.Create(textReader, settings))
        {
            return (T)serializer.Deserialize(xmlReader);
        }
    }
}

Invoke it like this

var resp = Deserialize<PICKUPREQUESTRESPONSE>(thexml);