Implement serialization

This commit is contained in:
Flamefire committed 2014-10-25 13:42:46 +02:00
1 parent 5e789d27a6
commit 527841b940
2 files changed
+134 -26

No files matched your search

+4 -1
View File
@@ -180,17 +180,20 @@ namespace Vocaluxe.Base
SThemeCover themeCover; SThemeCover themeCover;
using (var stream = new FileStream(path, FileMode.Open)) using (var stream = new FileStream(path, FileMode.Open))
themeCover = CXmlSerializer.Deserialize<SThemeCover>(stream); themeCover = CXmlSerializer.Deserialize<SThemeCover>(stream);
CXmlSerializer.Serialize(path + "2", themeCover);
string cover = themeCover.Info.Author; string cover = themeCover.Info.Author;
STheme theme = new STheme(); STheme theme = new STheme();
STheme theme2 = new STheme(); STheme theme2 = new STheme();
Stopwatch watch = new Stopwatch(); Stopwatch watch = new Stopwatch();
watch.Start(); watch.Start();
for (int i = 0; i < 10000; i++) for (int i = 0; i < 1; i++)
{ {
using (var stream = new StreamReader(Path.Combine(CBase.Themes.GetThemeScreensPath(-1), "ScreenMain.xml"))) using (var stream = new StreamReader(Path.Combine(CBase.Themes.GetThemeScreensPath(-1), "ScreenMain.xml")))
theme = CXmlSerializer.Deserialize<STheme>(stream); theme = CXmlSerializer.Deserialize<STheme>(stream);
} }
watch.Stop(); watch.Stop();
CXmlSerializer.Serialize(Path.Combine(CBase.Themes.GetThemeScreensPath(-1), "ScreenMain2.xml"),theme);
return;
Stopwatch watch2 = new Stopwatch(); Stopwatch watch2 = new Stopwatch();
watch2.Start(); watch2.Start();
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
+130 -25
View File
@@ -21,12 +21,23 @@ namespace VocaluxeLib.Xml
public static class CXmlSerializer public static class CXmlSerializer
{ {
/// <summary>
/// Uniform settings for writing XML files. ALWAYS use this!
/// </summary>
private static readonly XmlWriterSettings _XMLSettings = new XmlWriterSettings
{
Indent = true,
Encoding = Encoding.UTF8,
ConformanceLevel = ConformanceLevel.Document
};
private struct SFieldInfo private struct SFieldInfo
{ {
public string Name; public string Name;
public FieldInfo Info; public FieldInfo Info;
public object DefaultValue; public object DefaultValue;
public bool HasDefaultValue; public bool HasDefaultValue;
public bool IsAttribute;
public bool IsList; //List with child elements (<List><El/><El/></List>) public bool IsList; //List with child elements (<List><El/><El/></List>)
public bool IsEmbeddedList; //List w/o child elements(<List/><List/>) public bool IsEmbeddedList; //List w/o child elements(<List/><List/>)
public bool IsNullable; public bool IsNullable;
@@ -97,20 +108,23 @@ namespace VocaluxeLib.Xml
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>); return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
} }
private static bool _IsNullable(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
private static bool _IsList(this FieldInfo field) private static bool _IsList(this FieldInfo field)
{ {
return field.FieldType._IsList(); return field.FieldType._IsList();
} }
private static readonly Dictionary<Type, List<SFieldInfo>> _CacheFieldAttributes = new Dictionary<Type, List<SFieldInfo>>(); private static readonly Dictionary<Type, List<SFieldInfo>> _CacheFields = new Dictionary<Type, List<SFieldInfo>>();
private static readonly Dictionary<Type, List<SFieldInfo>> _CacheFieldFields = new Dictionary<Type, List<SFieldInfo>>();
private static readonly Dictionary<Type, String> _CacheTypeName = new Dictionary<Type, string>(); private static readonly Dictionary<Type, String> _CacheTypeName = new Dictionary<Type, string>();
private static List<SFieldInfo> _GetFields(Type type, bool attributes) private static List<SFieldInfo> _GetFieldInfos(Type type)
{ {
List<SFieldInfo> result; List<SFieldInfo> result;
if (attributes && _CacheFieldAttributes.TryGetValue(type, out result) || if (_CacheFields.TryGetValue(type, out result))
!attributes && _CacheFieldFields.TryGetValue(type, out result))
return result; return result;
result = new List<SFieldInfo>(); result = new List<SFieldInfo>();
FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
@@ -119,11 +133,12 @@ namespace VocaluxeLib.Xml
if (field._HasAttribute<XmlIgnoreAttribute>()) if (field._HasAttribute<XmlIgnoreAttribute>())
continue; continue;
XmlAttributeAttribute attribute = field._GetAttribute<XmlAttributeAttribute>(); XmlAttributeAttribute attribute = field._GetAttribute<XmlAttributeAttribute>();
if (attribute != null != attributes)
continue;
SFieldInfo info = new SFieldInfo {Info = field}; SFieldInfo info = new SFieldInfo {Info = field};
if (attribute != null) if (attribute != null)
{
info.IsAttribute = true;
info.Name = attribute.AttributeName; info.Name = attribute.AttributeName;
}
else else
{ {
XmlElementAttribute element = field._GetAttribute<XmlElementAttribute>(); XmlElementAttribute element = field._GetAttribute<XmlElementAttribute>();
@@ -134,7 +149,8 @@ namespace VocaluxeLib.Xml
XmlArrayAttribute array = field._GetAttribute<XmlArrayAttribute>(); XmlArrayAttribute array = field._GetAttribute<XmlArrayAttribute>();
if (array != null) if (array != null)
{ {
Debug.Assert(field._IsList()); Debug.Assert(field._IsList(), "Only lists can have the array attribute");
Debug.Assert(!info.IsAttribute, "Lists cannot be attributes");
info.Name = array.ElementName; info.Name = array.ElementName;
info.IsList = true; info.IsList = true;
} }
@@ -153,7 +169,7 @@ namespace VocaluxeLib.Xml
info.IsEmbeddedList = true; info.IsEmbeddedList = true;
} }
} }
else if (field.FieldType.GetGenericTypeDefinition() == typeof(Nullable<>)) else if (field.FieldType._IsNullable())
info.IsNullable = true; info.IsNullable = true;
info.SubType = field.FieldType.GetGenericArguments()[0]; info.SubType = field.FieldType.GetGenericArguments()[0];
} }
@@ -169,15 +185,22 @@ namespace VocaluxeLib.Xml
info.HasDefaultValue = true; info.HasDefaultValue = true;
info.DefaultValue = defAttr.Value; info.DefaultValue = defAttr.Value;
} }
else if (info.IsNullable)
{
info.HasDefaultValue = true;
info.DefaultValue = null;
}
result.Add(info); result.Add(info);
} }
if (attributes) _CacheFields.Add(type, result);
_CacheFieldAttributes.Add(type, result);
else
_CacheFieldFields.Add(type, result);
return result; return result;
} }
private static List<SFieldInfo> _GetFields(Type type, bool attributes)
{
return _GetFieldInfos(type).Where(f => f.IsAttribute == attributes).ToList();
}
private static string _GetTypeName(Type type) private static string _GetTypeName(Type type)
{ {
string name; string name;
@@ -196,6 +219,7 @@ namespace VocaluxeLib.Xml
return name; return name;
} }
#region Deserialization
private static object _GetValue(XmlNode node, Type type) private static object _GetValue(XmlNode node, Type type)
{ {
if (type.IsEnum) if (type.IsEnum)
@@ -226,20 +250,20 @@ namespace VocaluxeLib.Xml
if (subName != subNode.Name) if (subName != subNode.Name)
throw new XmlException("Invalid list entry '" + subNode.Name + "' in " + _FindXPath(node) + "; Expected: " + subName); throw new XmlException("Invalid list entry '" + subNode.Name + "' in " + _FindXPath(node) + "; Expected: " + subName);
object subValue = _GetValue(subNode, subType); object subValue = _GetValue(subNode, subType);
_ProcessChildNodes(subNode, ref subValue, true); _ReadChildNodes(subNode, ref subValue, true);
subValues.Add(subValue); subValues.Add(subValue);
} }
return _CreateList(type, subValues); return _CreateList(type, subValues);
} }
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) if (type._IsNullable())
{ {
Type subType = type.GetGenericArguments()[0]; Type subType = type.GetGenericArguments()[0];
return _GetValue(node, subType); return _GetValue(node, subType);
} }
object value = Activator.CreateInstance(type); object value = Activator.CreateInstance(type);
_ProcessChildNodes(node, ref value, true); _ReadChildNodes(node, ref value, true);
_ProcessChildNodes(node, ref value, false); _ReadChildNodes(node, ref value, false);
return value; return value;
} }
@@ -261,7 +285,7 @@ namespace VocaluxeLib.Xml
return value; return value;
} }
private static bool _CheckAndSetDefaultValue(ref object result, SFieldInfo field) private static bool _CheckAndSetDefaultValue(object result, SFieldInfo field)
{ {
if (field.IsEmbeddedList) if (field.IsEmbeddedList)
{ {
@@ -269,12 +293,12 @@ namespace VocaluxeLib.Xml
return true; return true;
} }
if (!field.HasDefaultValue) if (!field.HasDefaultValue)
return field.IsNullable; return false;
field.Info.SetValue(result, field.DefaultValue); field.Info.SetValue(result, field.DefaultValue);
return true; return true;
} }
private static object _CreateList(Type type, List<object> values) private static object _CreateList(Type type, ICollection values)
{ {
object list = Activator.CreateInstance(type, new object[] {values.Count}); object list = Activator.CreateInstance(type, new object[] {values.Count});
if (values.Count > 0) if (values.Count > 0)
@@ -286,12 +310,12 @@ namespace VocaluxeLib.Xml
return list; return list;
} }
private static void _AddList(object result, SFieldInfo listField, List<object> list) private static void _AddList(object result, SFieldInfo listField, ICollection list)
{ {
listField.Info.SetValue(result, _CreateList(listField.Info.FieldType, list)); listField.Info.SetValue(result, _CreateList(listField.Info.FieldType, list));
} }
private static void _ProcessChildNodes(XmlNode parent, ref object result, bool attributes) private static void _ReadChildNodes(XmlNode parent, ref object result, bool attributes)
{ {
IEnumerable nodes; IEnumerable nodes;
if (attributes) if (attributes)
@@ -335,7 +359,7 @@ namespace VocaluxeLib.Xml
field = fields[curField]; field = fields[curField];
if (field.Name == node.Name) if (field.Name == node.Name)
break; break;
if (!_CheckAndSetDefaultValue(ref result, field)) if (!_CheckAndSetDefaultValue(result, field))
throw new XmlException("Unexpected element: " + _FindXPath(node) + "; Expected: " + field.Name); throw new XmlException("Unexpected element: " + _FindXPath(node) + "; Expected: " + field.Name);
} }
if (curField >= fields.Count) if (curField >= fields.Count)
@@ -373,10 +397,66 @@ namespace VocaluxeLib.Xml
for (; curField < fields.Count; curField++) for (; curField < fields.Count; curField++)
{ {
SFieldInfo field = fields[curField]; SFieldInfo field = fields[curField];
if (!_CheckAndSetDefaultValue(ref result, field)) if (!_CheckAndSetDefaultValue(result, field))
throw new XmlException("element: " + field.Name + " is missing in " + _FindXPath(parent)); throw new XmlException("element: " + field.Name + " is missing in " + _FindXPath(parent));
} }
} }
#endregion Deserialization
#region Serialization
private static void _WriteNode(XmlWriter writer, string name, object value, bool isAttribute)
{
Type type = value.GetType();
if (type.IsEnum || type == typeof(string) || type.IsPrimitive || type._IsNullable())
{
string strVal = (string)Convert.ChangeType(value, typeof(string), CultureInfo.InvariantCulture);
if (isAttribute)
writer.WriteAttributeString(name, strVal);
else
writer.WriteElementString(name, strVal);
}
else if (type._IsList())
{
Debug.Assert(!isAttribute, "Lists cannot be attributes");
writer.WriteStartElement(name);
String subName = _GetTypeName(type.GetGenericArguments()[0]);
IEnumerable list = (IEnumerable)value;
foreach (object subValue in list)
{
writer.WriteStartElement(subName);
_WriteChildNodes(writer, subValue);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
else
{
Debug.Assert(!isAttribute, "Complex types cannot be attributes");
writer.WriteStartElement(name);
_WriteChildNodes(writer, value);
writer.WriteEndElement();
}
}
private static void _WriteChildNodes(XmlWriter writer, object o)
{
List<SFieldInfo> fields = _GetFieldInfos(o.GetType());
foreach (SFieldInfo field in fields)
{
object value = field.Info.GetValue(o);
if (field.HasDefaultValue && Equals(value, field.DefaultValue))
continue;
if (field.IsEmbeddedList)
{
IEnumerable values = (IEnumerable)value;
foreach (object subValue in values)
_WriteNode(writer, field.Name, subValue, field.IsAttribute);
}
else
_WriteNode(writer, field.Name, value, field.IsAttribute);
}
}
#endregion
public static T Deserialize<T>(XmlReader reader) where T : new() public static T Deserialize<T>(XmlReader reader) where T : new()
{ {
@@ -388,7 +468,7 @@ namespace VocaluxeLib.Xml
xDoc.Load(reader); xDoc.Load(reader);
if (xDoc.DocumentElement == null) if (xDoc.DocumentElement == null)
throw new XmlException("No root element found!"); throw new XmlException("No root element found!");
_ProcessChildNodes(xDoc.DocumentElement, ref result, false); _ReadChildNodes(xDoc.DocumentElement, ref result, false);
return (T)result; return (T)result;
} }
@@ -411,5 +491,30 @@ namespace VocaluxeLib.Xml
XmlResolver = null XmlResolver = null
}); });
} }
public static void Serialize(string filePath, object o)
{
XmlRootAttribute root = o.GetType()._GetAttribute<XmlRootAttribute>();
string name;
if (root != null && !string.IsNullOrEmpty(root.ElementName))
name = root.ElementName;
else
{
XmlTypeAttribute typeAtt = o.GetType()._GetAttribute<XmlTypeAttribute>();
if (typeAtt != null && !string.IsNullOrEmpty(typeAtt.TypeName))
name = typeAtt.TypeName;
else
name = "root";
}
using (XmlWriter writer = XmlWriter.Create(filePath, _XMLSettings))
{
writer.WriteStartDocument();
writer.WriteStartElement(name);
_WriteChildNodes(writer, o);
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
}
} }
} }