Wednesday, February 22, 2012

Default XmlSerializer for Web Api do not to support complex object?

I am trying to post an xml of a model with nested objects and the XmlSerializer for Asp.Net Web Api only deserialize the first level property.

I found a solution to my problem but I am sure I am not doing it right.

Here is an example of the xml I am trying to post and the C# classes it supposed to deserialize to.
<customer>
  <customerid>100</customerid>
  <orders>
  <order><orderid>1</orderid><amount>1</amount></order>
  <order><orderid>2</orderid><amount>2</amount></order>
  </orders>
</customer>
public class Customer
{
  public Customer()
  {
    Orders = new List();
  }

  public string CustomerId { get; set; }

  public List Orders { get; set; }
}

public class Order
{
  public string OrderId { get; set; }
  public int Amount { get; set; }
}

The only thing I get is CustomerId = 100. Orders count is 0. If I switch the Content-Type to application/json then it deserialized correctly.

After many trials and errors, I noticed that the Type parameter of the OnReadFromStreamAsync in XmlSerializer is IKeyValueModel instead of Customer like I expected.

I ended up writing my own XmlFormatter and replace the default one with mine. I added this two lines to my Application_Start method.

GlobalConfiguration.Configuration.Formatters.Add(new CustomXmlFormatter());
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);

Here is mine CustomXmlFormatter class.

  public CustomXmlFormatter()
  {
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
    Encoding = new UTF8Encoding(false, true);
  }

  protected override bool CanReadType(Type type)
  {
    if (type == (Type)null)
      throw new ArgumentNullException("type");

    if (type == typeof(IKeyValueModel))
      return false;

    return true;
  }

  protected override bool CanWriteType(Type type)
  {
    return true;
  }

  protected override Task OnReadFromStreamAsync(Type type, Stream stream, 
    HttpContentHeaders contentHeaders, FormatterContext formatterContext)
  {
    return Task.Factory.StartNew(() =>
    {
      using (var streamReader = new StreamReader(stream, Encoding))
      {
        var serializer = new XmlSerializer(type);
        return serializer.Deserialize(streamReader);
      }
    });
  }

  protected override Task OnWriteToStreamAsync(Type type, object value, Stream stream, 
    HttpContentHeaders contentHeaders, FormatterContext formatterContext, 
    System.Net.TransportContext transportContext)
  {
    var serializer = new XmlSerializer(type);
    return Task.Factory.StartNew(() =>
    {
      using (var streamWriter = new StreamWriter(stream, Encoding))
      {
        serializer.Serialize(streamWriter, value);
      }
    });
  }
}

No comments: