1

I have a simple class as a test:

public class TEST
{
     public string PropertyOne { get; set; }
     public string PropertyTwo { get; set; }
}

I want to serialize this class to XML, but when I do; I want to specify at runtime how I want to serialize it. Only PropertyOne, only PropertyTwo or both.

I figured out you can achieve it normally by using the [XmlIgnore] attribute on the properties that you don't want to serialize. I then tried the method explained here:

How to add property-level Attribute to the TypeDescriptor at runtime?

But for some reason it simply doesn't work. Here's my code:

TEST testInstance = new TEST();
testInstance.PropertyOne = "One";
testInstance.PropertyTwo = "Two";

SetAttributesToObjectOnProperty(testInstance, "PropertyOne");

using (StringWriter mainStringWriter = new StringWriter())
{
    XmlSerializer mainXMLSerializer = new XmlSerializer(testInstance.GetType());
    mainXMLSerializer.Serialize(mainStringWriter, testInstance);
    string theXMLToWrite = mainStringWriter.ToString();
}

public static void SetAttributesToObjectOnProperty(object classInstanceToCheck, string propertyName)
{
        if (propertyName != null && classInstanceToCheck != null)
        {
            // Prepare the Property Overriding Type Descriptor
            PropertyOverridingTypeDescriptor mainPropertyOverridingTypeDescriptor = new PropertyOverridingTypeDescriptor(TypeDescriptor.GetProvider(classInstanceToCheck).GetTypeDescriptor(classInstanceToCheck));

            // Iterate through properies in the supplied Object / Type
            foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(classInstanceToCheck))
            {
                if (propertyDescriptor.Name == propertyName)
                {
                    System.Xml.Serialization.XmlIgnoreAttribute theXMLIgnoreAttributToAdd = new System.Xml.Serialization.XmlIgnoreAttribute();

                    PropertyDescriptor newPropertyDescriptor = TypeDescriptor.CreateProperty(classInstanceToCheck.GetType(), propertyDescriptor, theXMLIgnoreAttributToAdd);

                    // Set the new PropertyOverridingTypeDescriptor to override that property
                    mainPropertyOverridingTypeDescriptor.OverrideProperty(newPropertyDescriptor);

                    break;
                }
            }

            // Add the new descriptor provider that will return our descriptor instead of default
            TypeDescriptor.AddProvider(new TypeDescriptorOverridingProvider(mainPropertyOverridingTypeDescriptor), classInstanceToCheck);
        }
}



    public static void SetAttributesToObjectOnProperty(object classInstanceToCheck, string propertyName)
    {
        try
        {
            if (propertyName != null)
            {
                // Prepare the Property Overriding Type Descriptor
                PropertyOverridingTypeDescriptor mainPropertyOverridingTypeDescriptor = new PropertyOverridingTypeDescriptor(TypeDescriptor.GetProvider(classInstanceToCheck).GetTypeDescriptor(classInstanceToCheck));

                // Iterate through properies in the supplied Object / Type
                foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(classInstanceToCheck))
                {
                    if (propertyDescriptor.Name == propertyName)
                    {
                        System.Xml.Serialization.XmlIgnoreAttribute theXMLIgnoreAttributToAdd = new System.Xml.Serialization.XmlIgnoreAttribute();

                        PropertyDescriptor newPropertyDescriptor = TypeDescriptor.CreateProperty(classInstanceToCheck.GetType(), propertyDescriptor, theXMLIgnoreAttributToAdd);

                        // Set the new PropertyOverridingTypeDescriptor to override that property
                        mainPropertyOverridingTypeDescriptor.OverrideProperty(newPropertyDescriptor);

                        break;
                    }
                }

                // Add the new descriptor provider that will return our descriptor instead of default
                TypeDescriptor.AddProvider(new TypeDescriptorOverridingProvider(mainPropertyOverridingTypeDescriptor), classInstanceToCheck);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error at 'SetAttributesToObjectOnProperty'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }

    public class PropertyOverridingTypeDescriptor : CustomTypeDescriptor
    {
        // Fields
        private readonly Dictionary<string, PropertyDescriptor> OverridePropertyDescriptors = new Dictionary<string, PropertyDescriptor>();

        // Constructor
        public PropertyOverridingTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { }

        // Methods
        public void OverrideProperty(PropertyDescriptor thePropertyDescriptor)
        {
            try
            {
                OverridePropertyDescriptors[thePropertyDescriptor.Name] = thePropertyDescriptor;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error at 'OverrideProperty'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        public override object GetPropertyOwner(PropertyDescriptor thePropertyDescriptor)
        {
            try
            {
                object toReturn = base.GetPropertyOwner(thePropertyDescriptor);

                if (toReturn == null)
                {
                    return this;
                }

                return toReturn;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error at 'GetPropertyOwner'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return null;
            }
        }
        public PropertyDescriptorCollection GetPropertiesMain(PropertyDescriptorCollection thePropertyDescriptorCollection)
        {
            try
            {
                List<PropertyDescriptor> mainPropertyDescriptorList = new List<PropertyDescriptor>(thePropertyDescriptorCollection.Count + 1);

                foreach (PropertyDescriptor propertyDescriptor in thePropertyDescriptorCollection)
                {
                    if (OverridePropertyDescriptors.ContainsKey(propertyDescriptor.Name))
                    {
                        mainPropertyDescriptorList.Add(OverridePropertyDescriptors[propertyDescriptor.Name]);
                    }
                    else
                    {
                        mainPropertyDescriptorList.Add(propertyDescriptor);
                    }
                }

                PropertyDescriptorCollection toReturn = new PropertyDescriptorCollection(mainPropertyDescriptorList.ToArray());

                return toReturn;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error at 'GetPropertiesMain'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return null;
            }
        }
        public override PropertyDescriptorCollection GetProperties()
        {
            try
            {
                return GetPropertiesMain(base.GetProperties());
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error at 'GetProperties'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return null;
            }
        }
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            try
            {
                return GetPropertiesMain(base.GetProperties(attributes));
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error at 'GetProperties'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return null;
            }
        }
    }

    public class TypeDescriptorOverridingProvider : TypeDescriptionProvider
    {
        // Fields
        private readonly ICustomTypeDescriptor MainCustomTypeDescriptor;

        // Constructor
        public TypeDescriptorOverridingProvider(ICustomTypeDescriptor theCustomTypeDescriptor)
        {
            try
            {
                this.MainCustomTypeDescriptor = theCustomTypeDescriptor;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error at Constructor 'TypeDescriptorOverridingProvider'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        // Methods
        public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
        {
            try
            {
                return MainCustomTypeDescriptor;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error at 'GetTypeDescriptor'" + Environment.NewLine + Environment.NewLine + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return null;
            }
        }
    }

There are no exceptions, it just still serializes both.

I don't know if I'm not using this method correctly or if I'm missing something else here.

I would like to use this method on other bigger classifications and try use other XML Attributes as well.

Shiasu-sama
  • 1,179
  • 2
  • 12
  • 39
  • 1
    `XmlSerializer` doesn't make use of runtime type descriptor providers, perhaps because the serializer is generated once and cached statically. Instead, you need to use [`XmlAttributeOverrides`](https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlattributeoverrides?view=netframework-4.7.2) as shown in e.g. [Ignore a property during xml serialization but not during deserialization](https://stackoverflow.com/q/18242320) or [How to add XmlInclude attribute dynamically](https://stackoverflow.com/q/2689566/3744182). – dbc Jan 31 '19 at 12:24
  • @dbc I've just tested this and it works great :). – Shiasu-sama Jan 31 '19 at 12:37
  • 1
    Glad to help. Be sure to cache the serializers thereby created statically for future reuse, otherwise you will get a nasty memory leak. See [Memory Leak using StreamReader and XmlSerializer](https://stackoverflow.com/q/23897145/3744182) for details. – dbc Jan 31 '19 at 12:59

0 Answers0