public static void SavePropertiesToXmlStream(
            this IDictionary propertyBag,
            XmlWriter writer,
            XmlWriterSettings settings,
            string name,
            Dictionary <string, string> settingNameToDescriptionMap)
        {
            if (propertyBag == null)
            {
                throw new ArgumentNullException(nameof(propertyBag));
            }

            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            Type   propertyBagType;
            string propertyBagTypeName;

            propertyBagType     = propertyBag.GetType();
            propertyBagTypeName = propertyBagType.Name;

            if (propertyBagTypeName != "PropertyBag")
            {
                propertyBagTypeName = NormalizeTypeName(propertyBag.GetType().FullName);
            }

            writer.WriteStartElement(PROPERTIES_ID);
            if (name != null)
            {
                writer.WriteAttributeString(KEY_ID, name);
            }

            if (propertyBagTypeName != "PropertyBag")
            {
                writer.WriteAttributeString(TYPE_ID, propertyBagTypeName);
            }

            string[] tokens = new string[propertyBag.Keys.Count];
            propertyBag.Keys.CopyTo(tokens, 0);

            Array.Sort <string>(tokens);

            foreach (string key in tokens)
            {
                object property = propertyBag[key];

                StringSet stringSet = property as StringSet;
                if (stringSet != null)
                {
                    SaveSet(writer, stringSet, key);
                    continue;
                }

                IntegerSet integerSet = property as IntegerSet;
                if (integerSet != null)
                {
                    SaveSet(writer, integerSet, key);
                    continue;
                }

                IDictionary pb = property as IDictionary;
                if (pb != null)
                {
                    ((IDictionary)pb).SavePropertiesToXmlStream(writer, settings, key, settingNameToDescriptionMap);
                    continue;
                }

                if (settingNameToDescriptionMap != null)
                {
                    string description;

                    if (settingNameToDescriptionMap.TryGetValue(key, out description))
                    {
                        writer.WriteComment(description);
                    }
                }

                writer.WriteStartElement(PROPERTY_ID);
                writer.WriteAttributeString(KEY_ID, key);

                Type          propertyType = property.GetType();
                TypeConverter tc           = TypeDescriptor.GetConverter(propertyType);
                writer.WriteAttributeString(VALUE_ID, tc.ConvertToString(property));

                if (propertyType != typeof(string))
                {
                    string typeName = NormalizeTypeName(propertyType.FullName);
                    writer.WriteAttributeString(TYPE_ID, typeName);
                }

                writer.WriteEndElement(); // KeyValuePair
            }
            writer.WriteEndElement();     // Properties
        }
        public static void LoadPropertiesFromXmlStream(this IDictionary propertyBag, XmlReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }

            if (propertyBag == null)
            {
                throw new ArgumentNullException(nameof(propertyBag));
            }

            while (reader.IsStartElement(PROPERTIES_ID) || reader.IsStartElement(PROPERTY_ID))
            {
                string key   = null;
                string value = null;
                bool   isEmpty;

                if (reader.IsStartElement(PROPERTIES_ID))
                {
                    key = reader.GetAttribute(KEY_ID);

                    string typeName = reader.GetAttribute(TYPE_ID);

                    IDictionary nestedPropertyBag;

                    if (string.IsNullOrEmpty(typeName))
                    {
                        nestedPropertyBag = new PropertiesDictionary();
                    }
                    else
                    {
                        Type type = GetPropertiesDictionaryType(typeName);
                        nestedPropertyBag = (IDictionary)Activator.CreateInstance(type);
                    }

                    propertyBag[key] = nestedPropertyBag;
                    isEmpty          = reader.IsEmptyElement;
                    if (!isEmpty)
                    {
                        reader.ReadStartElement(PROPERTIES_ID);
                        LoadPropertiesFromXmlStream(nestedPropertyBag, reader);
                        reader.ReadEndElement();
                    }
                    else
                    {
                        reader.Read();
                    }
                }
                else
                {
                    key   = reader.GetAttribute(KEY_ID);
                    value = reader.GetAttribute(VALUE_ID);
                    string typeName = reader.GetAttribute(TYPE_ID);
                    isEmpty = reader.IsEmptyElement;

                    if (typeName == STRING_SET_ID ||
                        typeName == INTEGER_SET_ID)
                    {
                        if (typeName == STRING_SET_ID)
                        {
                            StringSet set = new StringSet();
                            propertyBag[key] = set;
                            LoadSet(set, reader);
                        }
                        else
                        {
                            IntegerSet set = new IntegerSet();
                            propertyBag[key] = set;
                            LoadSet(set, reader);
                        }

                        if (!isEmpty)
                        {
                            reader.ReadEndElement();
                        }
                        continue;
                    }

                    reader.ReadStartElement(PROPERTY_ID);
                    Type propertyType = GetPropertiesDictionaryType(typeName);

                    if (typeName == "System.Version")
                    {
                        Version version;
                        bool    succeeded = Version.TryParse(value, out version);
                        Debug.Assert(succeeded);
                        propertyBag[key] = version;
                        continue;
                    }

                    TypeConverter tc = TypeDescriptor.GetConverter(propertyType);
                    Debug.Assert(tc.CanConvertFrom(typeof(string)));

                    object propertyValue = tc.ConvertFromString(value);
                    propertyBag[key] = propertyValue;

                    Debug.Assert(isEmpty);
                }
            }
        }