Rails compatible .NET xml serialization
I am writing an ASP.NET MVC 2 site with R开发者_高级运维EST endpoints. I want to consume this service with ActiveResource in a Rails web application.
So far, I've managed to get routes setup in ASP.NET MVC that match the ActiveResource conventions. But where I'm having problems is with de-serializing the data that ActiveResource sends to the REST service.
It seems that Rails does xml serialization very different than .NET (but I haven't been able to find these conventions documented anywhere). For example, with a .NET class like this:
public class Person
{
public string first_name { get; set; }
public string last_name { get; set; }
public bool active { get; set; }
}
Here are some of the differences:
- .NET serializes the root as <Person> while rails does <person>
- .NET adds a namespace while rails does not
- .NET serializes the properties like <first_name> while rails does <first-name>
- .NET serializes the bool as <active> while rails does <active type="boolean">
Number 4 doesn't seem to cause any problems, but the other 3 cause .NET to not de-serialize the message. You can solve these problems by decorating the object with XmlRoot and XmlElement attributes. But this is error prone and tedious.
Does anyone know a better way to de-serialize XML in .NET which has been serialized by Rails' ActiveResource?
This can be achieved using XmlAttributeOverrides.
Following is a basic example, GetOverrides()
being the important part:
public class RailsXmlSerializer
{
private Type type;
public RailsXmlSerializer(Type type)
{
this.type = type;
}
public void Serialize(Stream stream, object o)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer xmlSerializer = new XmlSerializer(type, GetOverrides(type));
xmlSerializer.Serialize(stream, o, ns);
}
public object Deserialize(Stream stream)
{
XmlSerializer xmlSerializer = new XmlSerializer(type, GetOverrides(type));
return xmlSerializer.Deserialize(stream);
}
private XmlAttributeOverrides GetOverrides(Type type)
{
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes classAttributes = new XmlAttributes();
classAttributes.XmlType = new XmlTypeAttribute(type.Name.ToLower());
overrides.Add(type, classAttributes);
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(type))
{
XmlAttributes propertyAttributes = new XmlAttributes();
propertyAttributes.XmlElements.Add(new XmlElementAttribute(property.Name.ToLower().Replace('_', '-')));
overrides.Add(type, property.Name, propertyAttributes);
}
return overrides;
}
}
Obviously, this needs to be a more robust implementation, possibly as a decorator for XmlSerializer, with error and type checking. Of course, GetOverrides()
can also be customized to provide the specific output you desire. That being said, here's the resulting output given a sample Person
object:
<?xml version="1.0" encoding="utf-16"?>
<person>
<first-name>Jason</first-name>
<last-name>Bourne</last-name>
<active>true</active>
</person>
And, given this input, deserialization is also performed correctly to provide the proper Person object.
yes.
[XmlType("person", // issue #1
Namespace="")] // issue #2
public class Person
{
[XmlElement("first-name")] // issue #3
public string first_name { get; set; }
[XmlElement("last-name")] // issue #3
public string last_name { get; set; }
public bool active { get; set; }
}
精彩评论