Implement serialization
This commit is contained in:
1 parent
5e789d27a6
commit
527841b940
2 files changed
+134
-26
No files matched your search
@@ -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++)
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user