madks13
madks13

Reputation: 155

List<String> not deserializing properly

I have a custom Option class that holds objects, with some additional parameters (name, info...). I'm trying to serialize a list of Option in order to save settings. All value types, and custom serializable classes seem to work fine, but a List doesn't deserialize properly. All i get is a System.Xml.XmlNode instead of List. The node contains the values from the list, they simply are not in a list.

Here is the relevant part of the Option class :

#region DefaultValue

    private object _defaultObject = null;

   [SettingsSerializeAs(System.Configuration.SettingsSerializeAs.Binary)]
    public object DefaultValue
    {
        get { return _defaultObject; }
        set
        {
            _defaultObject = value;
            NotifyPropertyChanged("DefaultValue");
        }
    }

    #endregion

    #region Value

    private object _object = null;

   [SettingsSerializeAs(System.Configuration.SettingsSerializeAs.Binary)]
    public object Value
    {
        get { return _object; }
        set
        {
            if (DefaultValue == null)
            {
                DefaultValue = value;
                _object = value;
                NotifyPropertyChanged("Value");
            }
            else if (DefaultValue.GetType().IsAssignableFrom(value.GetType()))
            {
                _object = value;
                NotifyPropertyChanged("Value");
            }
        }
    }

Here is how i add the List to a List (the second one is for comparison, and works fine without the first) :

Add(new Option() { Name = "ModuleFolders", DisplayName = "Module folders", Value = new List<String>() { ".\\Modules", "..\\Modules" }, Group = "Session", Info = "This is the list of folders containing all of the modules", ShortInfo = "Paths to the module folders"});
        Add(new Option() { Name = "ModulePattern", DisplayName = "Module pattern", Value = "GAME.Modules.*.dll", Group = "Session", Info = "This is the pattern used to find module assemblies by file name", ShortInfo = "Pattern for module names", IsReadOnly = true});

And finally, here is the resulting xml :

<?xml version="1.0"?>
<ArrayOfOption xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Option>
    <DefaultValue xsi:type="ArrayOfString">
      <string>.\Modules</string>
      <string>..\Modules</string>
    </DefaultValue>
    <Value xsi:type="ArrayOfString">
      <string>.\Modules</string>
      <string>..\Modules</string>
    </Value>
    <Name>ModuleFolders</Name>
    <DisplayName>Module folders</DisplayName>
    <ShortInfo>Paths to the module folders</ShortInfo>
    <Info>This is the list of folders containing all of the modules</Info>
    <Group>Session</Group>
    <IsReadOnly>false</IsReadOnly>
  </Option>
  <Option>
    <DefaultValue xsi:type="xsd:string">GAME.Modules.*.dll</DefaultValue>
    <Value xsi:type="xsd:string">GAME.Modules.*.dll</Value>
    <Name>ModulePattern</Name>
    <DisplayName>Module pattern</DisplayName>
    <ShortInfo>Pattern for module names</ShortInfo>
    <Info>This is the pattern used to find module assemblies by file name</Info>
    <Group>Session</Group>
    <IsReadOnly>true</IsReadOnly>
  </Option>
</ArrayOfOption>

I can't find why the serializer isn't converting back the list properly. There is no error message either.

If needed, here is the GitHub link to the project.

Upvotes: 0

Views: 846

Answers (1)

madks13
madks13

Reputation: 155

After lots of searching and experimenting i found an answer, so i'm putting it here in case someone else has the same problem :

  • First i used XMLSerializer to serialize/deserialize objects, but when errors happened, there was only a line on the output saying something went wrong, which wasn't very helpful.
  • I tried enabling exception throwing on all XML serialization errors, but none was thrown.
  • Then i switched to DataContractSerializer, which gave me more information on what was going on.

Turns out, for unknown types, such as List<String> (weirdly enough, MSDN says it is a known type), one has to provide the type to the serializer AND the deserializer. With DataContractSerializer, it's done by adding [KnownType(typeof(List<String>))] to the definition of the object using it.

In my case, this is how my class begins :

[Serializable]
[DataContract]
[KnownType(typeof(List<String>))]
[KnownType(typeof(DoubleInterval))]
public class Option : IOption
{
}

The DataContractSerializer will use the attributes to map out objects to the XML data.

Upvotes: 1

Related Questions