diff --git a/VocaluxeLib/Xml/CTypeExtensions.cs b/VocaluxeLib/Xml/CTypeExtensions.cs index 1d7c6c5a..a2427c52 100644 --- a/VocaluxeLib/Xml/CTypeExtensions.cs +++ b/VocaluxeLib/Xml/CTypeExtensions.cs @@ -48,6 +48,7 @@ namespace VocaluxeLib.Xml public bool IsAttribute; public bool IsList; //List with child elements () public bool IsEmbeddedList; //List w/o child elements() + public bool IsDictionary; //List where the element names are the keys public bool IsNullable; public bool IsNormalized; public XmlRangedAttribute Ranged; @@ -88,7 +89,7 @@ namespace VocaluxeLib.Xml XmlArrayAttribute array = field.GetAttribute(); if (array != null) { - Debug.Assert(field.IsList() || field.FieldType.IsArray, "Only lists and arrays can have the array attribute"); + Debug.Assert(field.FieldType.IsList() || field.FieldType.IsArray, "Only lists and arrays can have the array attribute"); Debug.Assert(!info.IsAttribute, "Lists cannot be attributes"); info.Name = array.ElementName; info.IsList = true; @@ -102,10 +103,10 @@ namespace VocaluxeLib.Xml info.AltName = altName.AltName; if (field.FieldType.IsGenericType) - info.SubType = field.FieldType.GetGenericArguments()[0]; + info.SubType = field.FieldType.GetGenericArguments().Last(); if (field.FieldType.IsArray) info.SubType = field.FieldType.GetElementType(); - if (field.IsList() || field.FieldType.IsArray) + if (field.FieldType.IsList() || field.FieldType.IsArray) { if (!info.IsList) { @@ -121,6 +122,11 @@ namespace VocaluxeLib.Xml } else if (field.FieldType.IsNullable()) info.IsNullable = true; + else if (field.FieldType.IsDictionary()) + { + Debug.Assert(field.FieldType.GetGenericArguments()[0] == typeof(string), "Keys of dictionaries must be strings"); + info.IsDictionary = true; + } if (field.HasAttribute()) { @@ -128,12 +134,13 @@ namespace VocaluxeLib.Xml info.IsNormalized = true; } info.Ranged = field.GetAttribute(); - Debug.Assert(info.Ranged == null || field.FieldType == typeof(int), "Only ints can be ranged"); + Debug.Assert(info.Ranged == null || field.FieldType == typeof(int) || field.FieldType == typeof(float) || field.FieldType == typeof(double), + "Only ints,floats and double can be ranged"); DefaultValueAttribute defAttr = field.GetAttribute(); if (defAttr != null) { - Debug.Assert(!field.IsList(), "Lists cannot have a default value"); + Debug.Assert(!field.FieldType.IsList(), "Lists cannot have a default value"); info.HasDefaultValue = true; info.DefaultValue = defAttr.Value; } @@ -187,14 +194,14 @@ namespace VocaluxeLib.Xml return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } + public static bool IsDictionary(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>); + } + public static bool IsList(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>); } - - public static bool IsList(this FieldInfo field) - { - return field.FieldType.IsList(); - } } } \ No newline at end of file diff --git a/VocaluxeLib/Xml/CXmlSerializer.cs b/VocaluxeLib/Xml/CXmlSerializer.cs index 0a5aab48..41daa05c 100644 --- a/VocaluxeLib/Xml/CXmlSerializer.cs +++ b/VocaluxeLib/Xml/CXmlSerializer.cs @@ -117,11 +117,24 @@ namespace VocaluxeLib.Xml if (subName != subNode.Name && !subNode.Name.StartsWith(subName)) throw new XmlException("Invalid list entry '" + subNode.Name + "' in " + _GetXPath(node) + "; Expected: " + subName); object subValue = _GetValue(subNode, subType); - _ReadChildNodes(subNode, ref subValue, true); subValues.Add(subValue); } return _CreateList(type, subValues); } + if (type.IsDictionary()) + { + Type subType = type.GetGenericArguments()[1]; + object dict = Activator.CreateInstance(type); + MethodInfo add = type.GetMethod("Add"); + foreach (XmlNode subNode in node.ChildNodes) + { + if (subNode is XmlComment) + continue; + object subValue = _GetValue(subNode, subType); + add.Invoke(dict, new object[] {subNode.Name, subValue}); + } + return dict; + } object value; try @@ -270,10 +283,18 @@ namespace VocaluxeLib.Xml !field.IsNullable && !((float)value).IsInRange(0, 1)) throw new XmlException("Value in " + _GetXPath(node) + " is not normalized. (Value=" + value + ")"); } - if (field.Ranged != null) + if (field.Ranged != null && value != null) { - if (field.IsNullable && !((int?)value).Value.IsInRange(field.Ranged.Min, field.Ranged.Max) || - !field.IsNullable && !((int)value).IsInRange(field.Ranged.Min, field.Ranged.Max)) + bool ok = false; + if (field.Info.FieldType == typeof(int)) + ok = ((int)value).IsInRange(field.Ranged.Min, field.Ranged.Max); + else if (field.Info.FieldType == typeof(float)) + ok = ((float)value).IsInRange(field.Ranged.Min, field.Ranged.Max); + else if (field.Info.FieldType == typeof(double)) + ok = ((double)value).IsInRange(field.Ranged.Min, field.Ranged.Max); + else + Debug.Assert(false, "Ranged attribute has invalid type"); + if (!ok) { throw new XmlException("Value in " + _GetXPath(node) + " is not in the range " + field.Ranged.Min + "-" + field.Ranged.Max + ". (Value=" + value + ")"); @@ -324,6 +345,8 @@ namespace VocaluxeLib.Xml else writer.WriteElementString(name, strVal); } + else if (type.IsNullable()) + _WriteNode(writer, name, type.GetGenericArguments()[0], value, isAttribute, arrayItemName); else if (type.IsList() || type.IsArray) { Debug.Assert(!isAttribute, "Lists cannot be attributes"); @@ -335,8 +358,16 @@ namespace VocaluxeLib.Xml _WriteNode(writer, subName, subType, subValue, false); writer.WriteEndElement(); } - else if (type.IsNullable()) - _WriteNode(writer, name, type.GetGenericArguments()[0], value, isAttribute, arrayItemName); + else if (type.IsDictionary()) + { + Debug.Assert(!isAttribute, "Dictionaries cannot be attributes"); + writer.WriteStartElement(name); + Type subType = type.GetGenericArguments()[1]; + IDictionary dict = (IDictionary)value; + foreach (DictionaryEntry entry in dict) + _WriteNode(writer, (string)entry.Key, subType, entry.Value, false); + writer.WriteEndElement(); + } else { Debug.Assert(!isAttribute, "Complex types cannot be attributes");