Search code examples
c#serializationdatacontractdatamembernetdatacontractserializer

Force order in NetDataContractSerializer without using DataContractAttribute


I want to force the NetDataContractSerializer to write property values in a specific order, because the serializer writes them in an alphabetic order.

I know I can achieve this by adding [DataMember(Order = X)] attributes to these properties but this works only when I add [DataContract] attribute to the class I am serializing.

Unfortunately, I can't add the [DataContract] attribute to this class since its base class doesn't have one.

Is there another way to force the order?


Solution

  • I've came up with the idea of using own ISerializationSurrogate where I can handle getting the properties that are being serialized myself.

    private class MySerializationSurrogate<T> : ISerializationSurrogate
    {
        private IEnumerable<DataMemberAttribute> GetXmlAttribs(PropertyInfo p) => p.GetCustomAttributes(false).OfType<DataMemberAttribute>();
    
        public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context)
        {
            var myobj = (T)obj;
            foreach (var property in myobj.GetType().GetProperties().Where(p => GetXmlAttribs(p).Any()).OrderBy(p => GetXmlAttribs(p).First().Order))
            {
                info.AddValue(property.Name, property.GetValue(myobj));
            }
        }
    
        public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
        {
            var myobj = (T)obj;
            foreach (var property in myobj.GetType().GetProperties().Where(p => GetXmlAttribs(p).Any()).OrderBy(p => GetXmlAttribs(p).First().Order))
            {
                property.SetValue(myobj, info.GetValue(property.Name, property.PropertyType));
            }
            return null;
        }
    }
    

    Then I assign the serialization surrogate to the serializer.

    var formatter = new NetDataContractSerializer();
    var surrogateSelector = new SurrogateSelector();
    surrogateSelector.AddSurrogate(typeof(T), new StreamingContext(StreamingContextStates.All), new MySerializationSurrogate<T>());
    formatter.SurrogateSelector = surrogateSelector;
    

    And everything works like hell.

    NOTE that T is the type of the object being serialized/deserialized.