How to XML serialize ExpandoObject in property / value pairs?

I am intercepting a Web API 2 pipeline when I call OnActionExecuted. Here I am returning the object returned by the action to an ExpandoObject, recursively (that is, any properties of the object that are themselves objects also become ExpandoObjects, etc. Down the hierarchy).

It's XML serialized OK, but only as a dictionary (presumably because ExpandoObject implements IDictionary and it just fetches keys and values ​​from it). I would prefer it to be serialized as if it were an object with properties rather than as a group of key / value pairs.

How can I do this without creating my own XML serializer?

+3


source to share


1 answer


You can wrap ExpandoObject

in an implementation ISerializable

. It wraps the contained one recursively ExpandoObject

.

[Serializable]
public class SerializableWrapper : ISerializable
{
    private IDictionary<string, object> _data;

    public IDictionary<string, object> Data
    {
        get { return _data; }
    }

    public SerializableWrapper(IDictionary<string, object> data)
    {
        _data = data;
    }

    protected SerializableWrapper(SerializationInfo info, StreamingContext context)
    {
        this._data = new Dictionary<string, object>();
        var enumerator = info.GetEnumerator();
        while (enumerator.MoveNext())
        {
            this._data[enumerator.Name] = enumerator.Value;
        }
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {            
        foreach (var kvp in this._data)
        {
            info.AddValue(kvp.Key, Wrap(kvp.Value));
        }
    }

    private static object Wrap(object value)
    {
        var expando = value as ExpandoObject;
        if (expando != null)
        {
            return new SerializableWrapper(expando);
        }
        var expandoList = value as IEnumerable<ExpandoObject>;
        if (expandoList != null)
        {
            return expandoList
                .Select(Wrap)
                .Cast<SerializableWrapper>()
                .ToArray();
        }
        var list = value as IEnumerable;
        if (list != null && !(value is string))
        {
            return list
                .Cast<object>()
                .Select(Wrap)
                .ToArray();
        }
        return value;
    }
}

      

dynamic obj = new ExpandoObject();
obj.Foo = 3;
obj.Bar = new [] { new ExpandoObject() };
obj.Bar[0].Baz = "Qux";

var wrapped = new SerializableWrapper(obj);

var ser = new DataContractSerializer(typeof(SerializableWrapper), new [] { typeof(SerializableWrapper[]), typeof(object[]) });
var mem = new MemoryStream();
ser.WriteObject(mem, wrapped);

      

Forms:

<SerializableWrapper xmlns="http://schemas.datacontract.org/2004/07/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema">
  <Foo i:type="x:int" xmlns="">3</Foo>
  <Bar i:type="a:ArrayOfSerializableWrapper" xmlns="" xmlns:a="http://schemas.datacontract.org/2004/07/">
    <a:SerializableWrapper>
      <Baz i:type="x:string">Qux</Baz>
    </a:SerializableWrapper>
  </Bar>
</SerializableWrapper>

      



Serialized XML is not very good. You can play with DataContractResolver

or manipulate the XML to make it less ugly.

To deserialize it again, you can use

mem.Position = 0;
var deserialized = (SerializableWrapper) ser.ReadObject(mem);

      

Another approach would be to implement IXmlSerializable

and use instead XmlSerializer

.

+2


source







All Articles