Search code examples
c#asp.net-core-3.1output-formatting

difference between XmlSerializerOutputFormatter and XmlDataContractSerializerOutputFormatter


I am wondering what is the difference between these two serializers. when setting accept header = application/xml. Am using Plain DTOs as return values, Which one is preferred? Also consumer of the api who request xml in response which should be used?

Am working on aspnet core web api 3.1, building restful apis. Any suggestions/redirects on the above query will be helpful.


Solution

  • The XmlSerializerOutputFormatter is an asp.net core outputformatter that uses the XmlSerializer internally, whereas the DataContractSerializerOutputFormatter uses the DataContractSerializer internally.

    The DataContractSerializer is more flexible in configuration. For example it supports reference detection to prevent the serializer from recursively serializing items, which would normally cause an endless loop.

    In my own projects, I prefer to use the DataContractSerializerOutputFormatter because it's able to cope with properties with private setter

    public string Text { get; private set; }
    

    Failing case

    Dtos project

    namespace DataContractSerializerPOC.Dtos
    {
        public class Person
        {
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
    
            // Fullname can only be set from this project
            public string FullName { get; internal set; }
        }
    
        public class PersonService
        {
            public List<Person> GetPeople()
            {
                // Create a list of people to be serialized
                var people = new List<Person>
                {
                    new Person { Id = 1, FirstName = "John", LastName = "Doe" },
                    new Person { Id = 2, FirstName = "Jim", LastName = "Flix" },
                    new Person { Id = 3, FirstName = "Jack", LastName = "Splick" },
                };
                
                // Set the fullname from this project
                // In some cases you may need to do this, instead of implementing a readonly property
                foreach (var person in people)
                    person.FullName = $"{person.FirstName} {person.LastName}";
    
                return people;
            }
        }
    }
    

    Console project

    namespace DataContractSerializerPOC
    {
        class Program
        {
            static void Main(string[] args)
            {
                var personService = new PersonService();
                var people = personService.GetPeople();
    
                var writer = new StringWriter();
                var serializer = new XmlSerializer(typeof(List<Person>));
                serializer.Serialize(writer, people);
            }
        }
    }
    

    Result

    Cannot deserialize type because it contains property which has no public setter

    Working case with DataContractSerializer

    Dtos project

    namespace DataContractSerializerPOC.Dtos
    {
        [DataContract]
        public class Person
        {
            [DataMember]
            public int Id { get; set; }
            [DataMember]
            public string FirstName { get; set; }
            [DataMember]
            public string LastName { get; set; }
    
            // Fullname can only be set from this project
            [DataMember]
            public string FullName { get; internal set; }
        }
    
        public class PersonService
        {
            ...
        }
    }
    

    Console project

    namespace DataContractSerializerPOC
    {
        class Program
        {
            static void Main(string[] args)
            {
                var personService = new PersonService();
                var people = personService.GetPeople();
    
                var memoryStream = new MemoryStream();
                var serializer = new DataContractSerializer(typeof(List<Person>));
                serializer.WriteObject(memoryStream, people);
    
                memoryStream.Seek(0, SeekOrigin.Begin);
                var text = new StreamReader(memoryStream).ReadToEnd();
            }
        }
    }
    

    Result

    Resulting XML

    So the DataContractSerializer is able to deal with properties with a private setter, whilst the XmlSerializer isn't.