More options to serialize dictionaries

This commit is contained in:
Flamefire committed 2014-10-30 16:32:39 +01:00
1 parent 61a0d4fdf1
commit 0c5dcd9c74
3 files changed
+129 -27

No files matched your search

+18 -1
View File
@@ -93,6 +93,23 @@ namespace VocaluxeLib.Xml
private static readonly Dictionary<Type, List<SFieldInfo>> _CacheFields = new Dictionary<Type, List<SFieldInfo>>();
private static readonly Dictionary<Type, String> _CacheTypeName = new Dictionary<Type, string>();
/// <summary>
/// Gets the name of the subType (e.g. list/array element) if any
/// </summary>
/// <param name="type">Xml type name of the subType or null if no subType</param>
/// <returns></returns>
public static string GetSubTypeName(this Type type)
{
Type subType;
if (type.IsGenericType)
subType = type.GetGenericArguments().Last();
else if (type.IsArray)
subType = type.GetElementType();
else
return null;
return subType.GetTypeName();
}
private static void _FillInfo(ref SFieldInfo info, MemberInfo field)
{
XmlAttributeAttribute attribute = field.GetAttribute<XmlAttributeAttribute>();
@@ -126,7 +143,7 @@ namespace VocaluxeLib.Xml
if (info.Type.IsGenericType)
info.SubType = info.Type.GetGenericArguments().Last();
if (info.Type.IsArray)
else if (info.Type.IsArray)
info.SubType = info.Type.GetElementType();
if (info.Type.IsList() || info.Type.IsArray)
{
+80 -23
View File
@@ -83,7 +83,7 @@ namespace VocaluxeLib.Xml
#endregion Debug Helpers
#region Deserialization
private object _GetValue(XmlNode node, Type type, string arrayItemName = null)
private object _GetValue(XmlNode node, Type type, string arrayItemName = null, object result = null)
{
if (type.IsEnum)
{
@@ -106,35 +106,55 @@ namespace VocaluxeLib.Xml
if (!node.HasChildNodes)
return null;
Type subType = type.GetGenericArguments()[0];
return _GetValue(node, subType);
return _GetValue(node, subType, arrayItemName, result);
}
if (type.IsList() || type.IsArray)
{
Type subType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0];
String subName = arrayItemName ?? subType.GetTypeName();
subName = subName.ToLowerInvariant();
List<object> subValues = new List<object>();
foreach (XmlNode subNode in node.ChildNodes)
{
if (subNode is XmlComment)
continue;
if (subName != subNode.Name && !subNode.Name.StartsWith(subName))
if (subName == subNode.Name.ToLowerInvariant() && !subNode.Name.ToLowerInvariant().StartsWith(subName))
throw new XmlException("Invalid list entry '" + subNode.Name + "' in " + _GetXPath(node) + "; Expected: " + subName);
object subValue = _GetValue(subNode, subType);
subValues.Add(subValue);
}
return _CreateList(type, subValues);
if (result == null)
return _CreateList(type, subValues);
_FillList(result, type, subValues);
return result;
}
if (type.IsDictionary())
{
Type subType = type.GetGenericArguments()[1];
object dict = Activator.CreateInstance(type);
object dict = result ?? Activator.CreateInstance(type);
MethodInfo add = type.GetMethod("Add");
if (arrayItemName != null)
arrayItemName = arrayItemName.ToLowerInvariant();
foreach (XmlNode subNode in node.ChildNodes)
{
if (subNode is XmlComment)
continue;
object subValue = _GetValue(subNode, subType);
add.Invoke(dict, new object[] {subNode.Name, subValue});
string subName;
if (arrayItemName != null)
{
if (arrayItemName != subNode.Name.ToLowerInvariant() && !subNode.Name.ToLowerInvariant().StartsWith(arrayItemName))
throw new XmlException("Invalid dictionary entry '" + subNode.Name + "' in " + _GetXPath(node) + "; Expected: " + arrayItemName);
if (subNode.Attributes == null)
throw new XmlException("'name' attribute is missing in " + _GetXPath(subNode));
XmlNode nameAtt = subNode.Attributes.GetNamedItem("name");
if (nameAtt == null)
throw new XmlException("'name' attribute is missing in " + _GetXPath(subNode));
subName = nameAtt.Value;
}
else
subName = subNode.Name;
add.Invoke(dict, new object[] {subName, subValue});
}
return dict;
}
@@ -142,7 +162,7 @@ namespace VocaluxeLib.Xml
object value;
try
{
value = Activator.CreateInstance(type);
value = result ?? Activator.CreateInstance(type);
}
catch (Exception)
{
@@ -193,23 +213,29 @@ namespace VocaluxeLib.Xml
return true;
}
private static object _CreateList(Type type, ICollection values)
private static void _FillList(object list, Type type, ICollection values)
{
if (values.Count <= 0)
return;
if (type.IsArray)
{
Array array = Array.CreateInstance(type.GetElementType(), values.Count);
Array array = (Array)list;
int i = 0;
foreach (object value in values)
array.SetValue(value, i++);
return array;
}
object list = Activator.CreateInstance(type, new object[] {values.Count});
if (values.Count > 0)
else
{
MethodInfo addMethod = type.GetMethod("Add");
foreach (object value in values)
addMethod.Invoke(list, new object[] {value});
}
}
private static object _CreateList(Type type, ICollection values)
{
object list = type.IsArray ? Array.CreateInstance(type.GetElementType(), values.Count) : Activator.CreateInstance(type, new object[] {values.Count});
_FillList(list, type, values);
return list;
}
@@ -315,7 +341,7 @@ namespace VocaluxeLib.Xml
#endregion Deserialization
#region Serialization
private void _WriteNode(XmlWriter writer, string name, Type type, object value, bool isAttribute, string arrayItemName = null)
private void _WriteNode(XmlWriter writer, string name, Type type, object value, bool isAttribute, string arrayItemName = null, string nameAttribute = null)
{
if (!isAttribute && _GetCommentCallback != null)
{
@@ -337,14 +363,23 @@ namespace VocaluxeLib.Xml
if (isAttribute)
writer.WriteAttributeString(name, strVal);
else
writer.WriteElementString(name, strVal);
{
writer.WriteStartElement(name);
if (nameAttribute != null)
writer.WriteAttributeString("name", nameAttribute);
if (!string.IsNullOrEmpty(strVal))
writer.WriteValue(strVal);
writer.WriteEndElement();
}
}
else if (type.IsNullable())
_WriteNode(writer, name, type.GetGenericArguments()[0], value, isAttribute, arrayItemName);
_WriteNode(writer, name, type.GetGenericArguments()[0], value, isAttribute, arrayItemName, nameAttribute);
else if (type.IsList() || type.IsArray)
{
Debug.Assert(!isAttribute, "Lists cannot be attributes");
writer.WriteStartElement(name);
if (nameAttribute != null)
writer.WriteAttributeString("name", nameAttribute);
Type subType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0];
String subName = arrayItemName ?? subType.GetTypeName();
IEnumerable list = (IEnumerable)value;
@@ -356,16 +391,34 @@ namespace VocaluxeLib.Xml
{
Debug.Assert(!isAttribute, "Dictionaries cannot be attributes");
writer.WriteStartElement(name);
if (nameAttribute != null)
writer.WriteAttributeString("name", nameAttribute);
Type subType = type.GetGenericArguments()[1];
IDictionary dict = (IDictionary)value;
foreach (DictionaryEntry entry in dict)
_WriteNode(writer, (string)entry.Key, subType, entry.Value, false);
{
string subName;
string subNameAttribute;
if (arrayItemName == null)
{
subName = (string)entry.Key;
subNameAttribute = null;
}
else
{
subName = arrayItemName;
subNameAttribute = (string)entry.Key;
}
_WriteNode(writer, subName, subType, entry.Value, false, null, subNameAttribute);
}
writer.WriteEndElement();
}
else
{
Debug.Assert(!isAttribute, "Complex types cannot be attributes");
writer.WriteStartElement(name);
if (nameAttribute != null)
writer.WriteAttributeString("name", nameAttribute);
if (value != null)
_WriteChildNodes(writer, value);
writer.WriteEndElement();
@@ -405,7 +458,7 @@ namespace VocaluxeLib.Xml
throw new XmlException("No root element found!");
try
{
_ReadChildNodes(xDoc.DocumentElement, ref result, false);
result = _GetValue(xDoc.DocumentElement, typeof(T), typeof(T).GetSubTypeName(), result);
}
catch (TargetInvocationException e)
{
@@ -476,12 +529,16 @@ namespace VocaluxeLib.Xml
else
name = "root";
}
writer.WriteStartDocument();
writer.WriteStartElement(name);
_WriteChildNodes(writer, o);
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
try
{
writer.WriteStartDocument();
_WriteNode(writer, name, o.GetType(), o, false, o.GetType().GetSubTypeName());
writer.WriteEndDocument();
}
finally
{
writer.Flush();
}
}
public void Serialize(string filePath, object o)
+31 -3
View File
@@ -393,6 +393,26 @@ namespace VocaluxeTests
Assert.AreEqual(_XmlIgnore, xml.Serialize(bar2));
}
[TestMethod]
public void TestOnlyList()
{
const string s = _Head + @"<root>
<String>2</String>
<String>3</String>
</root>";
_AssertSerDeserMatch<List<string>>(s);
}
[TestMethod]
public void TestOnlyDict()
{
const string s = _Head + @"<root>
<String name=""val1"">2</String>
<String name=""val2"">3</String>
</root>";
_AssertSerDeserMatch<Dictionary<string, string>>(s);
}
[TestMethod]
public void TestRealFiles()
{
@@ -402,11 +422,19 @@ namespace VocaluxeTests
{
string xmlPath = Path.Combine(filePath, type.Name + ".xml");
var xml = new CXmlSerializer(type == typeof(CConfig.SConfig));
object foo = xml.Deserialize(xmlPath, Activator.CreateInstance(type));
Assert.IsInstanceOfType(foo, type);
object foo = null;
try
{
foo = xml.Deserialize(xmlPath, Activator.CreateInstance(type));
}
catch (Exception e)
{
Assert.Fail("Exception with " + type.Name + ": {0}", new object[] {e.Message});
}
Assert.IsInstanceOfType(foo, type, "Wrong type with " + type.Name);
string newXml = xml.Serialize(foo);
string oldXml = File.ReadAllText(xmlPath);
Assert.AreEqual(oldXml, newXml, "Error in " + type.Name);
Assert.AreEqual(oldXml, newXml, "Error with " + type.Name);
}
}
}