Search code examples
c#xmlxmlserializer

XmlSerializer without xmlns in descendant classes


I need to serialize in XML a class that contains a List<> implemented with different concrete classes. I need an XML without xmlns references.

this is my example:

[Serializable]
public class Root
{
    [XmlElement("Tables")]
    public List<AbstractTable> Tables { get; set; } = new List<AbstractTable>();
}

[XmlInclude(typeof(TableType1))]
[XmlInclude(typeof(TableType2))]
[Serializable]
public class AbstractTable
{
    [XmlAttribute]
    public string TableName { get; set; }
}

[Serializable]
public class TableType1 : AbstractTable
{
    public TableType1()
    {
        TableName = "TableType1"; 
    }

    [XmlAttribute]
    public string ValueA { get; set; }
}

[Serializable]
public class TableType2 : AbstractTable
{
    public TableType2()
    {
        TableName = "TableType2"; 
    }

    [XmlAttribute]
    public string ValueB { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var allegato = new Root();            

        var tableType1 = new TableType1();
        tableType1.ValueA = "AAA";
        allegato.Tables.Add(tableType1);

        var tableType2 = new TableType2();
        tableType2.ValueB = "BBB";
        allegato.Tables.Add(tableType2);

        var string2Write = SerializeToString(allegato);
        File.WriteAllText(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Test.xml"), string2Write);
    }

    public static string SerializeToString<T>(T value)
    {
        var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
        var serializer = new XmlSerializer(value.GetType());
        var settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.OmitXmlDeclaration = true;

        using (var stream = new StringWriter())
        using (var writer = XmlWriter.Create(stream, settings))
        {
            serializer.Serialize(writer, value, emptyNamespaces);
            return stream.ToString();
        }
    }
}

I got SerializeToString from StackOverflow but works only for container class.

The problem is I am not able to remove xmlns from XML, this is what i get:

<Root>
  <Tables p2:type="TableType1" TableName="TableType1" ValueA="AAA" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance" />
  <Tables p2:type="TableType2" TableName="TableType2" ValueB="BBB" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance" />
</Root>

Solution

  • Use XmlSerializerNamespaces with Empty namespace: You already did this for the Root class. This should help prevent any unwanted namespace prefixes from being included in the root element, but it might not completely stop the xmlns:p2 for the concrete types (TableType1, TableType2).

    Remove XmlInclude on the abstract base class: Instead of using the XmlInclude attribute on the base class (AbstractTable), you can use the XmlArrayItem attribute directly in the Root class to indicate the possible types.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml;
    using System.Xml.Serialization;
    
    [Serializable]
    public class Root
    {
        [XmlArray("Tables")]
        [XmlArrayItem("TableType1", typeof(TableType1))]
        [XmlArrayItem("TableType2", typeof(TableType2))]
        public List<AbstractTable> Tables { get; set; } = new List<AbstractTable>();
    }
    
    [Serializable]
    public abstract class AbstractTable
    {
        [XmlAttribute]
        public string TableName { get; set; }
    }
    
    [Serializable]
    public class TableType1 : AbstractTable
    {
        public TableType1()
        {
            TableName = "TableType1";
        }
    
        [XmlAttribute]
        public string ValueA { get; set; }
    }
    
    [Serializable]
    public class TableType2 : AbstractTable
    {
        public TableType2()
        {
            TableName = "TableType2";
        }
    
        [XmlAttribute]
        public string ValueB { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var allegato = new Root();
    
            var tableType1 = new TableType1();
            tableType1.ValueA = "AAA";
            allegato.Tables.Add(tableType1);
    
            var tableType2 = new TableType2();
            tableType2.ValueB = "BBB";
            allegato.Tables.Add(tableType2);
    
            var string2Write = SerializeToString(allegato);
            File.WriteAllText(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Test.xml"), string2Write);
        }
    
        public static string SerializeToString<T>(T value)
        {
            var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
            var serializer = new XmlSerializer(value.GetType());
            var settings = new XmlWriterSettings
            {
                Indent = true,
                OmitXmlDeclaration = true
            };
    
            using (var stream = new StringWriter())
            using (var writer = XmlWriter.Create(stream, settings))
            {
                serializer.Serialize(writer, value, emptyNamespaces);
                return stream.ToString();
            }
        }
    }