Search code examples
c#xmldictionarydeserializationxmlserializer

C# - How to XML Deserialize a Dictionary<string>, List<string>>?


Here is what I tried but I get this error:

Error CS0029 Cannot implicitly convert type 'ListBoxAndDictionary.CountriesAndCities' to 'System.Collections.Generic.Dictionary<string, System.Collections.Generic.List>'

Here is my code:

private Dictionary<string, List<string>> dictCountryAndCities = new
      Dictionary<string, List<string>>();

private void button12_Click(object sender, EventArgs e)
{
    SaveFileDialog sfd = new SaveFileDialog();
    sfd.FileName = "CountriesAndCities";
    sfd.DefaultExt = ".xml";
    sfd.Filter = "XML (*.xml)|*.xml|All Files (*.*)|*.*";
    var sfdResult = sfd.ShowDialog();
    if (sfdResult == DialogResult.Cancel) 
        return;
    var container = new CountriesAndCities
    {
        DictCountriesAndCities = dictCountryAndCities
    };
    using (var writer = XmlWriter.Create(sfd.FileName)) 
        (new XmlSerializer(typeof(CountriesAndCities))).Serialize(writer, container);
}

private void button11_Click(object sender, EventArgs e)
{
    OpenFileDialog ofd = new OpenFileDialog();
    ofd.DefaultExt = ".xml";
    ofd.Filter = "XML (*.xml)|*.xml|All Files (*.*)|*.*";
    var ofdResult = ofd.ShowDialog();
    if (ofdResult == DialogResult.Cancel) 
        return;
    dictCountryAndCities = new Dictionary<string, List<string>>();

    XmlSerializer serializer = new XmlSerializer(typeof(CountriesAndCities));
    using (var reader = new StreamReader(ofd.FileName))
    {
        dictCountryAndCities = serializer.Deserialize(reader) as CountriesAndCities;

        //Error CS0029 Cannot implicitly convert type 'ListBoxAndDictionary.CountriesAndCities' to 'System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>>'
    }

    //binding DataSource for listBox1
    listBox1.DataSource = dictCountryAndCities.Keys.ToList();
}

[Serializable]
[XmlRoot("CountriesAndCities")]
public class CountriesAndCities
{
    public List<Country> Name
    {
        get { return DictCountriesAndCities.Select(x => new Country { CountryName = x.Key, Cities = x.Value }).ToList(); }
        set { DictCountriesAndCities = value.ToDictionary(x => x.CountryName, x => x.Cities); }
    }
    [XmlIgnore]
    public Dictionary<string, List<string>> DictCountriesAndCities { get; set; }
}

[Serializable]
public class Country
{
    public string CountryName { get; set; }
    public List<string> Cities { get; set; }
}

Solution

  • You have a few problems with your XML serialization code.

    Firstly, you need to modify your CountriesAndCities DTO as follows, replacing the List<Country> Name surrogate property with a Country [] Name property instead:

    [XmlRoot("CountriesAndCities")]
    public class CountriesAndCities
    {
        public Country [] Name      // Replace the List<Country> with Country []
        {
            get { return DictCountriesAndCities.Select(x => new Country { CountryName = x.Key, Cities = x.Value }).ToArray(); }
            set { DictCountriesAndCities = value?.ToDictionary(x => x.CountryName, x => x.Cities); }
        }
        [XmlIgnore]
        public Dictionary<string, List<string>> DictCountriesAndCities { get; set; }
    }
    
    public class Country
    {
        public string CountryName { get; set; }
        public List<string> Cities { get; set; }
    }
    

    Secondly, modify your serialization code as follows:

    var container = new CountriesAndCities
    {
        DictCountriesAndCities = dictCountriesAndCities,
    };
    using (var writer = XmlWriter.Create(sfd.FileName, new XmlWriterSettings { Indent = true })) // Indentation is for visualization purposes only
        (new XmlSerializer(typeof(CountriesAndCities))).Serialize(writer, container);
    

    Finally, change your deserialization code as follows:

    using (var reader = new StreamReader(ofd.FileName))
    {
        dictCountriesAndCities = ((new XmlSerializer(typeof(CountriesAndCities))).Deserialize(reader) as CountriesAndCities)
            ?.DictCountriesAndCities;
    }
    

    Notes:

    • Your "Error CS0029" is caused because you attempt to assign a value of type CountriesAndCities to a Dictionary<string, List<string>>. Instead, you must access the property DictCountriesAndCities.

    • In your method button11_Click() you are deserializing your XML into a local variable dictCountryAndCities, then binding to its keys:

       dictCountryAndCities = new Dictionary<string, List<string>>();
      
       XmlSerializer serializer = new XmlSerializer(typeof(CountriesAndCities));
       using (var reader = new StreamReader(ofd.FileName))
       {
           dictCountryAndCities = serializer.Deserialize(reader) as CountriesAndCities;
      
           //Error CS0029 Cannot implicitly convert type 'ListBoxAndDictionary.CountriesAndCities' to 'System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>>'
       }
      

      You are not updating the class member dictCountryAndCities at all. This seems wrong. However, it's not clear from your question what you want to be doing there, so I can't really recommend a fix.

    • The [Serializable] attribute is not required or used by XmlSerializer, and can be removed.

    • For an explanation as to why you should use Country [] instead of List<Country> for your surrogate Name property, see this answer to Cannot deserialize XML into a list using XML Deserializer.

    Demo fiddle here.