开发者

C# XML Deserialization W/ Default Values

开发者 https://www.devze.com 2022-12-15 10:58 出处:网络
I\'ve got an object that is being serialized / deserialized via the XmlSerializer in C#, .NET 3.5. One of the properties (and more in the future) is a collection: List where T is an enum value. This s

I've got an object that is being serialized / deserialized via the XmlSerializer in C#, .NET 3.5. One of the properties (and more in the future) is a collection: List where T is an enum value. This serializes / deserializes fine.

We are also using a "default values" mechanism to provide default values for the object, in case the serialized version doesn't have any value set. as a simple example, here is wha开发者_如何学编程t we are dong:

public enum MyEnum {
  Value1,
  Value2
}

public class Foo
{

  public List SomeSetting{ get; set; }

  public Foo()
  {
    SomeSetting = new List();
    SomeSetting.Add(MyEnum.Value1);
    SomeSetting.Add(MyEnum.Value2);
  }
}

This code works fine for setting the default values of SomeSetting when the object is constructed.

However, when we are deserializing an xml file that has values for SomeSetting, this default value setup is causing problems: the xml deserializer does not 'reset' the SomeSetting collection - it does not wipe it clean and populate with new data. Rather, it adds on to the data that is already there. So, if the xml file has Value1 serialized into it, when I deserialize that file, i end up with SomeSettings having {Value1, Value2, Value1} as the values being stored.

I need a way for the xml deserialization process to allow my default values to exist when there is no data for SomeSetting in the xml document, and also to wholesale replace the SomeSetting values when there is data in the xml document. How can I do this?

FYI - this is not the only property in the document. The document does exist, and is being serialized / deserialized for the other 'simple' values. This is the property that is causing problems, though. I have to support this scenario because I need to do this a lot, now.


FYI - i solved this with the IXMLSerializable interface. Note that this code is very specific to my needs in this one class, so YMMV.



        public void WriteXml(XmlWriter writer)
        {
            foreach (PropertyInfo prop in GetType().GetProperties())
            {
                XmlIgnoreAttribute attr;
                if (prop.TryGetAttribute(out attr))
                    continue;

                if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition().Equals(typeof(List)))
                {
                    XmlSerializer serializer = new XmlSerializer(prop.PropertyType, new XmlRootAttribute(prop.Name));
                    serializer.Serialize(writer, prop.GetValue(this, null));
                }
                else
                {
                    writer.WriteElementString(prop.Name, prop.GetValue(this, null).ToString());
                }
            }
        }

        public void ReadXml(XmlReader reader)
        {
            if (reader.IsEmptyElement)
                return;

            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(reader);

            Type type = GetType();

            foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
            {
                PropertyInfo prop = type.GetProperty(node.Name);
                if (prop != null && prop.CanWrite)
                {
                    object value;
                    if (prop.PropertyType.IsEnum)
                    {
                        string stringValue = node.InnerText;
                        value = Enum.Parse(prop.PropertyType, stringValue);
                    }
                    else if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition().Equals(typeof(List)))
                    {
                        Type enumType = prop.PropertyType.GetGenericArguments()[0];
                        value = Activator.CreateInstance(prop.PropertyType);
                        var addMethod = value.GetType().GetMethod("Add");
                        foreach (XmlNode subNode in node.ChildNodes)
                        {
                            object enumValue = Enum.Parse(enumType, subNode.InnerText);
                            addMethod.Invoke(value, new[] { enumValue });
                        }
                    }
                    else
                    {
                        string stringValue = node.InnerText;
                        value = Convert.ChangeType(stringValue, prop.PropertyType);
                    }
                    prop.SetValue(this, value, null);
                }
            }
        }

        public XmlSchema GetSchema()
        {
            return null;
        }


Quick and dirty is to implement ISupportInitialize. All .NET serializers (IIRC) respect this interface and will call EndInit after deserializing.

You can check the contents of your collection at this point and determine if you should add the defaults at that time. I'd also suggest checking to see if your instance has been initialized before first use and throwing an exception if it has not been. This will ensure other users of your class will know your instance must be initialized before use.


Yet another option rather than parsing the XML yourself, we could use the approach I used. Just a suggestion.

C# Xml Deserialize plus design suggestions

0

精彩评论

暂无评论...
验证码 换一张
取 消