Split serializer/deserializer
This commit is contained in:
1 parent
3928fee041
commit
12e48d0446
8 files changed
+438
-420
No files matched your search
@@ -250,7 +250,7 @@ namespace Vocaluxe.Base
|
||||
|
||||
private static void _LoadConfig()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
Config = xml.Deserialize<SConfig>(_FileConfig);
|
||||
|
||||
if (Config.Game.SongFolder.Length > 0)
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace Vocaluxe.Base.Fonts
|
||||
SDefaultFonts defaultFonts;
|
||||
try
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
defaultFonts = xml.Deserialize<SDefaultFonts>(Path.Combine(CSettings.ProgramFolder, CSettings.FolderNameFonts, CSettings.FileNameFonts));
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Vocaluxe.Base.ThemeSystem
|
||||
{
|
||||
try
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
_Data = xml.Deserialize<STheme>(Path.Combine(_Folder, _FileName));
|
||||
if (_Data.ThemeSystemVersion != _ThemeSystemVersion)
|
||||
{
|
||||
|
||||
@@ -60,10 +60,10 @@ namespace Vocaluxe.Lib.Playlist
|
||||
File = CHelper.GetUniqueFileName(Path.Combine(CSettings.DataFolder, CConfig.FolderPlaylists), filename + ".xml");
|
||||
}
|
||||
|
||||
SPlaylist data=new SPlaylist {Info = {Name = Name}, Songs = Songs.Select(plSong => plSong.ToStruct()).ToArray()};
|
||||
SPlaylist data = new SPlaylist {Info = {Name = Name}, Songs = Songs.Select(plSong => plSong.ToStruct()).ToArray()};
|
||||
|
||||
var xml = new CXmlSerializer();
|
||||
xml.Serialize(File,data);
|
||||
xml.Serialize(File, data);
|
||||
}
|
||||
|
||||
public bool _Load(string file)
|
||||
@@ -72,17 +72,17 @@ namespace Vocaluxe.Lib.Playlist
|
||||
SPlaylist data;
|
||||
try
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
data = xml.Deserialize<SPlaylist>(File);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CLog.LogError("Cannot load playlist from "+file+": "+e.Message);
|
||||
CLog.LogError("Cannot load playlist from " + file + ": " + e.Message);
|
||||
return false;
|
||||
}
|
||||
Name = data.Info.Name;
|
||||
Songs = new List<CPlaylistSong>();
|
||||
foreach (var songEntry in data.Songs)
|
||||
foreach (SPlaylistSong songEntry in data.Songs)
|
||||
{
|
||||
CSong plSong = CSongs.AllSongs.FirstOrDefault(song => song.Artist == songEntry.Artist && song.Title == songEntry.Title);
|
||||
if (plSong == null)
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace VocaluxeLib.Profile
|
||||
|
||||
public bool LoadProfile()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
try
|
||||
{
|
||||
xml.Deserialize(FilePath, this);
|
||||
@@ -106,11 +106,12 @@ namespace VocaluxeLib.Profile
|
||||
|
||||
private bool _ConvertProfile(ref Exception e)
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
var ser = new CXmlSerializer();
|
||||
try
|
||||
{
|
||||
var old = xml.Deserialize<SOldXmlProfile>(FilePath);
|
||||
string newXml = xml.Serialize(old.Info);
|
||||
string newXml = ser.Serialize(old.Info);
|
||||
xml.DeserializeString(newXml, this);
|
||||
}
|
||||
catch (Exception e2)
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace VocaluxeLib.Xml
|
||||
{
|
||||
public class CXmlDeserializer
|
||||
{
|
||||
#region Debug Helpers
|
||||
private string _GetXPath(XmlNode node)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (node != null)
|
||||
{
|
||||
switch (node.NodeType)
|
||||
{
|
||||
case XmlNodeType.Attribute:
|
||||
builder.Insert(0, "/@" + node.Name);
|
||||
node = ((XmlAttribute)node).OwnerElement;
|
||||
break;
|
||||
case XmlNodeType.Element:
|
||||
string index = _FindElementIndex(node);
|
||||
builder.Insert(0, "/" + node.Name + index);
|
||||
node = node.ParentNode;
|
||||
break;
|
||||
case XmlNodeType.Document:
|
||||
return builder.ToString();
|
||||
default:
|
||||
throw new ArgumentException("Only elements and attributes are supported");
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("Node was not in a document");
|
||||
}
|
||||
|
||||
private string _FindElementIndex(XmlNode element)
|
||||
{
|
||||
XmlNode parentNode = element.ParentNode;
|
||||
if (parentNode is XmlDocument)
|
||||
return "";
|
||||
XmlElement parent = (XmlElement)parentNode;
|
||||
int index = 1;
|
||||
if (parent != null)
|
||||
{
|
||||
XmlNode[] siblings = parent.ChildNodes.Cast<XmlNode>().Where(candidate => candidate is XmlElement && candidate.Name == element.Name).ToArray();
|
||||
foreach (XmlNode candidate in siblings)
|
||||
{
|
||||
if (candidate == element)
|
||||
return siblings.Length > 1 ? "[" + index + "]" : "";
|
||||
index++;
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("Couldn't find element within parent");
|
||||
}
|
||||
#endregion Debug Helpers
|
||||
|
||||
private object _GetValue(XmlNode node, Type type, string arrayItemName = null, object result = null)
|
||||
{
|
||||
if (type.IsEnum)
|
||||
{
|
||||
string stringValue = node.InnerText;
|
||||
try
|
||||
{
|
||||
return Enum.Parse(type, stringValue);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new XmlException("Invalid value in " + _GetXPath(node));
|
||||
}
|
||||
}
|
||||
if (type == typeof(string))
|
||||
return node.InnerText;
|
||||
if (type.IsPrimitive)
|
||||
return _GetPrimitiveValue(node, type);
|
||||
if (type.IsNullable())
|
||||
{
|
||||
if (!node.HasChildNodes)
|
||||
return null;
|
||||
Type subType = type.GetGenericArguments()[0];
|
||||
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.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);
|
||||
}
|
||||
if (result == null)
|
||||
return _CreateList(type, subValues);
|
||||
_FillList(result, type, subValues);
|
||||
return result;
|
||||
}
|
||||
if (type.IsDictionary())
|
||||
{
|
||||
Type subType = type.GetGenericArguments()[1];
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
object value;
|
||||
try
|
||||
{
|
||||
value = result ?? Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new XmlException("Could not create instance of " + _GetXPath(node) + "(Type=" + type.Name + ")");
|
||||
}
|
||||
_ReadChildNodes(node, ref value, true);
|
||||
_ReadChildNodes(node, ref value, false);
|
||||
return value;
|
||||
}
|
||||
|
||||
private object _GetPrimitiveValue(XmlNode node, Type type)
|
||||
{
|
||||
object value;
|
||||
string nodeVal = node.InnerText;
|
||||
try
|
||||
{
|
||||
int p = nodeVal.IndexOf(',');
|
||||
if (p > 0 && p >= nodeVal.Length - 3)
|
||||
{
|
||||
CBase.Log.LogError("German number format converted to english in " + _GetXPath(node));
|
||||
char[] tmp = nodeVal.ToCharArray();
|
||||
tmp[p] = '.';
|
||||
nodeVal = new string(tmp);
|
||||
}
|
||||
value = Convert.ChangeType(nodeVal, type, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
throw new XmlException("Invalid format in " + _GetXPath(node) + ": '" + nodeVal + "' (" + e.Message + ")");
|
||||
}
|
||||
catch (InvalidCastException e)
|
||||
{
|
||||
throw new XmlException(e.Message + " in " + _GetXPath(node) + ": '" + nodeVal + "'");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static bool _CheckAndSetDefaultValue(object result, SFieldInfo field)
|
||||
{
|
||||
if (field.IsEmbeddedList)
|
||||
{
|
||||
_AddList(result, field, new List<object>());
|
||||
return true;
|
||||
}
|
||||
if (!field.HasDefaultValue)
|
||||
return false;
|
||||
field.SetValue(result, field.DefaultValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void _FillList(object list, Type type, ICollection values)
|
||||
{
|
||||
if (values.Count <= 0)
|
||||
return;
|
||||
if (type.IsArray)
|
||||
{
|
||||
Array array = (Array)list;
|
||||
int i = 0;
|
||||
foreach (object value in values)
|
||||
array.SetValue(value, i++);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
private static void _AddList(object result, SFieldInfo listField, ICollection list)
|
||||
{
|
||||
listField.SetValue(result, _CreateList(listField.Type, list));
|
||||
}
|
||||
|
||||
private void _ReadChildNodes(XmlNode parent, ref object result, bool attributes)
|
||||
{
|
||||
IEnumerable nodes;
|
||||
if (attributes)
|
||||
nodes = parent.Attributes;
|
||||
else
|
||||
nodes = parent.ChildNodes;
|
||||
|
||||
List<SFieldInfo> fields = result.GetType().GetFields(attributes);
|
||||
|
||||
if (nodes != null)
|
||||
{
|
||||
//Dictionary of all embedded lists to allow interleaved/mixed elements
|
||||
Dictionary<string, Tuple<SFieldInfo, List<object>>> embLists = new Dictionary<string, Tuple<SFieldInfo, List<object>>>();
|
||||
foreach (XmlNode node in nodes)
|
||||
{
|
||||
if (node is XmlComment)
|
||||
continue;
|
||||
|
||||
SFieldInfo field = new SFieldInfo();
|
||||
int curField;
|
||||
for (curField = 0; curField < fields.Count; curField++)
|
||||
{
|
||||
field = fields[curField];
|
||||
if (field.Name == node.Name || field.AltName == node.Name || (field.IsEmbeddedList && node.Name.StartsWith(field.Name)))
|
||||
break;
|
||||
}
|
||||
if (curField >= fields.Count)
|
||||
{
|
||||
string msg = "Unexpected element: " + _GetXPath(node);
|
||||
if (fields.Count > 0)
|
||||
msg += " Expected: " + string.Join(", ", fields.Select(f => f.Name));
|
||||
throw new XmlException(msg);
|
||||
}
|
||||
|
||||
if (field.IsByteArray)
|
||||
{
|
||||
field.SetValue(result, Convert.FromBase64String(node.InnerText));
|
||||
fields.RemoveAt(curField);
|
||||
}
|
||||
else if (field.IsEmbeddedList)
|
||||
{
|
||||
Tuple<SFieldInfo, List<object>> entry;
|
||||
if (!embLists.TryGetValue(field.Name, out entry))
|
||||
{
|
||||
entry = new Tuple<SFieldInfo, List<object>>(field, new List<object>());
|
||||
embLists.Add(field.Name, entry);
|
||||
}
|
||||
object subValue = _GetValue(node, field.SubType);
|
||||
entry.Item2.Add(subValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
object value = _GetValue(node, field.Type, field.ArrayItemName);
|
||||
if (field.IsNormalized)
|
||||
{
|
||||
if (field.IsNullable && !((float?)value).Value.IsInRange(0, 1) ||
|
||||
!field.IsNullable && !((float)value).IsInRange(0, 1))
|
||||
throw new XmlException("Value in " + _GetXPath(node) + " is not normalized. (Value=" + value + ")");
|
||||
}
|
||||
if (field.Ranged != null && value != null)
|
||||
{
|
||||
bool ok = false;
|
||||
if (field.Type == typeof(int))
|
||||
ok = ((int)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
||||
else if (field.Type == typeof(float))
|
||||
ok = ((float)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
||||
else if (field.Type == 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 +
|
||||
")");
|
||||
}
|
||||
}
|
||||
field.SetValue(result, value);
|
||||
fields.RemoveAt(curField);
|
||||
}
|
||||
}
|
||||
//Add embedded lists
|
||||
foreach (Tuple<SFieldInfo, List<object>> entry in embLists.Values)
|
||||
{
|
||||
_AddList(result, entry.Item1, entry.Item2);
|
||||
fields.Remove(entry.Item1);
|
||||
}
|
||||
}
|
||||
foreach (SFieldInfo field in fields)
|
||||
{
|
||||
if (!_CheckAndSetDefaultValue(result, field))
|
||||
throw new XmlException("element: " + field.Name + " is missing in " + _GetXPath(parent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private T _Deserialize<T>(XmlReader reader, object result) where T : new()
|
||||
{
|
||||
if (reader.IsEmptyElement)
|
||||
return (T)result;
|
||||
|
||||
XmlDocument xDoc = new XmlDocument();
|
||||
xDoc.Load(reader);
|
||||
if (xDoc.DocumentElement == null)
|
||||
throw new XmlException("No root element found!");
|
||||
try
|
||||
{
|
||||
result = _GetValue(xDoc.DocumentElement, typeof(T), typeof(T).GetSubTypeName(), result);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw e.InnerException ?? e;
|
||||
}
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
public T DeserializeString<T>(string xml) where T : new()
|
||||
{
|
||||
return DeserializeString(xml, new T());
|
||||
}
|
||||
|
||||
public T DeserializeString<T>(string xml, T o) where T : new()
|
||||
{
|
||||
var reader = new XmlTextReader(new StringReader(xml))
|
||||
{
|
||||
WhitespaceHandling = WhitespaceHandling.Significant,
|
||||
Normalization = true,
|
||||
XmlResolver = null
|
||||
};
|
||||
try
|
||||
{
|
||||
return _Deserialize<T>(reader, o);
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string filePath) where T : new()
|
||||
{
|
||||
return Deserialize(filePath, new T());
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string filePath, T o) where T : new()
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
throw new FileNotFoundException(filePath);
|
||||
var reader = new XmlTextReader(filePath)
|
||||
{
|
||||
WhitespaceHandling = WhitespaceHandling.Significant,
|
||||
Normalization = true,
|
||||
XmlResolver = null
|
||||
};
|
||||
try
|
||||
{
|
||||
return _Deserialize<T>(reader, o);
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
@@ -35,312 +33,6 @@ namespace VocaluxeLib.Xml
|
||||
ConformanceLevel = ConformanceLevel.Document
|
||||
};
|
||||
|
||||
#region Debug Helpers
|
||||
private string _GetXPath(XmlNode node)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (node != null)
|
||||
{
|
||||
switch (node.NodeType)
|
||||
{
|
||||
case XmlNodeType.Attribute:
|
||||
builder.Insert(0, "/@" + node.Name);
|
||||
node = ((XmlAttribute)node).OwnerElement;
|
||||
break;
|
||||
case XmlNodeType.Element:
|
||||
string index = _FindElementIndex(node);
|
||||
builder.Insert(0, "/" + node.Name + index);
|
||||
node = node.ParentNode;
|
||||
break;
|
||||
case XmlNodeType.Document:
|
||||
return builder.ToString();
|
||||
default:
|
||||
throw new ArgumentException("Only elements and attributes are supported");
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("Node was not in a document");
|
||||
}
|
||||
|
||||
private string _FindElementIndex(XmlNode element)
|
||||
{
|
||||
XmlNode parentNode = element.ParentNode;
|
||||
if (parentNode is XmlDocument)
|
||||
return "";
|
||||
XmlElement parent = (XmlElement)parentNode;
|
||||
int index = 1;
|
||||
if (parent != null)
|
||||
{
|
||||
XmlNode[] siblings = parent.ChildNodes.Cast<XmlNode>().Where(candidate => candidate is XmlElement && candidate.Name == element.Name).ToArray();
|
||||
foreach (XmlNode candidate in siblings)
|
||||
{
|
||||
if (candidate == element)
|
||||
return siblings.Length > 1 ? "[" + index + "]" : "";
|
||||
index++;
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("Couldn't find element within parent");
|
||||
}
|
||||
#endregion Debug Helpers
|
||||
|
||||
#region Deserialization
|
||||
private object _GetValue(XmlNode node, Type type, string arrayItemName = null, object result = null)
|
||||
{
|
||||
if (type.IsEnum)
|
||||
{
|
||||
string stringValue = node.InnerText;
|
||||
try
|
||||
{
|
||||
return Enum.Parse(type, stringValue);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new XmlException("Invalid value in " + _GetXPath(node));
|
||||
}
|
||||
}
|
||||
if (type == typeof(string))
|
||||
return node.InnerText;
|
||||
if (type.IsPrimitive)
|
||||
return _GetPrimitiveValue(node, type);
|
||||
if (type.IsNullable())
|
||||
{
|
||||
if (!node.HasChildNodes)
|
||||
return null;
|
||||
Type subType = type.GetGenericArguments()[0];
|
||||
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.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);
|
||||
}
|
||||
if (result == null)
|
||||
return _CreateList(type, subValues);
|
||||
_FillList(result, type, subValues);
|
||||
return result;
|
||||
}
|
||||
if (type.IsDictionary())
|
||||
{
|
||||
Type subType = type.GetGenericArguments()[1];
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
object value;
|
||||
try
|
||||
{
|
||||
value = result ?? Activator.CreateInstance(type);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new XmlException("Could not create instance of " + _GetXPath(node) + "(Type=" + type.Name + ")");
|
||||
}
|
||||
_ReadChildNodes(node, ref value, true);
|
||||
_ReadChildNodes(node, ref value, false);
|
||||
return value;
|
||||
}
|
||||
|
||||
private object _GetPrimitiveValue(XmlNode node, Type type)
|
||||
{
|
||||
object value;
|
||||
string nodeVal = node.InnerText;
|
||||
try
|
||||
{
|
||||
int p = nodeVal.IndexOf(',');
|
||||
if (p > 0 && p >= nodeVal.Length - 3)
|
||||
{
|
||||
CBase.Log.LogError("German number format converted to english in " + _GetXPath(node));
|
||||
char[] tmp = nodeVal.ToCharArray();
|
||||
tmp[p] = '.';
|
||||
nodeVal = new string(tmp);
|
||||
}
|
||||
value = Convert.ChangeType(nodeVal, type, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
throw new XmlException("Invalid format in " + _GetXPath(node) + ": '" + nodeVal + "' (" + e.Message + ")");
|
||||
}
|
||||
catch (InvalidCastException e)
|
||||
{
|
||||
throw new XmlException(e.Message + " in " + _GetXPath(node) + ": '" + nodeVal + "'");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static bool _CheckAndSetDefaultValue(object result, SFieldInfo field)
|
||||
{
|
||||
if (field.IsEmbeddedList)
|
||||
{
|
||||
_AddList(result, field, new List<object>());
|
||||
return true;
|
||||
}
|
||||
if (!field.HasDefaultValue)
|
||||
return false;
|
||||
field.SetValue(result, field.DefaultValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void _FillList(object list, Type type, ICollection values)
|
||||
{
|
||||
if (values.Count <= 0)
|
||||
return;
|
||||
if (type.IsArray)
|
||||
{
|
||||
Array array = (Array)list;
|
||||
int i = 0;
|
||||
foreach (object value in values)
|
||||
array.SetValue(value, i++);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
private static void _AddList(object result, SFieldInfo listField, ICollection list)
|
||||
{
|
||||
listField.SetValue(result, _CreateList(listField.Type, list));
|
||||
}
|
||||
|
||||
private void _ReadChildNodes(XmlNode parent, ref object result, bool attributes)
|
||||
{
|
||||
IEnumerable nodes;
|
||||
if (attributes)
|
||||
nodes = parent.Attributes;
|
||||
else
|
||||
nodes = parent.ChildNodes;
|
||||
|
||||
List<SFieldInfo> fields = result.GetType().GetFields(attributes);
|
||||
|
||||
if (nodes != null)
|
||||
{
|
||||
//Dictionary of all embedded lists to allow interleaved/mixed elements
|
||||
Dictionary<string, Tuple<SFieldInfo, List<object>>> embLists = new Dictionary<string, Tuple<SFieldInfo, List<object>>>();
|
||||
foreach (XmlNode node in nodes)
|
||||
{
|
||||
if (node is XmlComment)
|
||||
continue;
|
||||
|
||||
SFieldInfo field = new SFieldInfo();
|
||||
int curField;
|
||||
for (curField = 0; curField < fields.Count; curField++)
|
||||
{
|
||||
field = fields[curField];
|
||||
if (field.Name == node.Name || field.AltName == node.Name || (field.IsEmbeddedList && node.Name.StartsWith(field.Name)))
|
||||
break;
|
||||
}
|
||||
if (curField >= fields.Count)
|
||||
{
|
||||
string msg = "Unexpected element: " + _GetXPath(node);
|
||||
if (fields.Count > 0)
|
||||
msg += " Expected: " + string.Join(", ", fields.Select(f => f.Name));
|
||||
throw new XmlException(msg);
|
||||
}
|
||||
|
||||
if (field.IsByteArray)
|
||||
{
|
||||
field.SetValue(result, Convert.FromBase64String(node.InnerText));
|
||||
fields.RemoveAt(curField);
|
||||
}
|
||||
else if (field.IsEmbeddedList)
|
||||
{
|
||||
Tuple<SFieldInfo, List<object>> entry;
|
||||
if (!embLists.TryGetValue(field.Name, out entry))
|
||||
{
|
||||
entry = new Tuple<SFieldInfo, List<object>>(field, new List<object>());
|
||||
embLists.Add(field.Name, entry);
|
||||
}
|
||||
object subValue = _GetValue(node, field.SubType);
|
||||
entry.Item2.Add(subValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
object value = _GetValue(node, field.Type, field.ArrayItemName);
|
||||
if (field.IsNormalized)
|
||||
{
|
||||
if (field.IsNullable && !((float?)value).Value.IsInRange(0, 1) ||
|
||||
!field.IsNullable && !((float)value).IsInRange(0, 1))
|
||||
throw new XmlException("Value in " + _GetXPath(node) + " is not normalized. (Value=" + value + ")");
|
||||
}
|
||||
if (field.Ranged != null && value != null)
|
||||
{
|
||||
bool ok = false;
|
||||
if (field.Type == typeof(int))
|
||||
ok = ((int)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
||||
else if (field.Type == typeof(float))
|
||||
ok = ((float)value).IsInRange(field.Ranged.Min, field.Ranged.Max);
|
||||
else if (field.Type == 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 +
|
||||
")");
|
||||
}
|
||||
}
|
||||
field.SetValue(result, value);
|
||||
fields.RemoveAt(curField);
|
||||
}
|
||||
}
|
||||
//Add embedded lists
|
||||
foreach (Tuple<SFieldInfo, List<object>> entry in embLists.Values)
|
||||
{
|
||||
_AddList(result, entry.Item1, entry.Item2);
|
||||
fields.Remove(entry.Item1);
|
||||
}
|
||||
}
|
||||
foreach (SFieldInfo field in fields)
|
||||
{
|
||||
if (!_CheckAndSetDefaultValue(result, field))
|
||||
throw new XmlException("element: " + field.Name + " is missing in " + _GetXPath(parent));
|
||||
}
|
||||
}
|
||||
#endregion Deserialization
|
||||
|
||||
#region Serialization
|
||||
private void _WriteNode(XmlWriter writer, string name, Type type, object value, bool isAttribute, string arrayItemName = null, string nameAttribute = null)
|
||||
{
|
||||
if (!isAttribute && _GetCommentCallback != null)
|
||||
@@ -445,75 +137,6 @@ namespace VocaluxeLib.Xml
|
||||
_WriteNode(writer, field.Name, field.Type, value, field.IsAttribute, field.ArrayItemName);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private T _Deserialize<T>(XmlReader reader, object result) where T : new()
|
||||
{
|
||||
if (reader.IsEmptyElement)
|
||||
return (T)result;
|
||||
|
||||
XmlDocument xDoc = new XmlDocument();
|
||||
xDoc.Load(reader);
|
||||
if (xDoc.DocumentElement == null)
|
||||
throw new XmlException("No root element found!");
|
||||
try
|
||||
{
|
||||
result = _GetValue(xDoc.DocumentElement, typeof(T), typeof(T).GetSubTypeName(), result);
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw e.InnerException ?? e;
|
||||
}
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
public T DeserializeString<T>(string xml) where T : new()
|
||||
{
|
||||
return DeserializeString(xml, new T());
|
||||
}
|
||||
|
||||
public T DeserializeString<T>(string xml, T o) where T : new()
|
||||
{
|
||||
var reader = new XmlTextReader(new StringReader(xml))
|
||||
{
|
||||
WhitespaceHandling = WhitespaceHandling.Significant,
|
||||
Normalization = true,
|
||||
XmlResolver = null
|
||||
};
|
||||
try
|
||||
{
|
||||
return _Deserialize<T>(reader, o);
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string filePath) where T : new()
|
||||
{
|
||||
return Deserialize(filePath, new T());
|
||||
}
|
||||
|
||||
public T Deserialize<T>(string filePath, T o) where T : new()
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
throw new FileNotFoundException(filePath);
|
||||
var reader = new XmlTextReader(filePath)
|
||||
{
|
||||
WhitespaceHandling = WhitespaceHandling.Significant,
|
||||
Normalization = true,
|
||||
XmlResolver = null
|
||||
};
|
||||
try
|
||||
{
|
||||
return _Deserialize<T>(reader, o);
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void _Serialize(object o, XmlWriter writer)
|
||||
{
|
||||
|
||||
@@ -106,15 +106,16 @@ namespace VocaluxeTests
|
||||
|
||||
private static void _AssertFail<T, T2>(String xmlString) where T2 : Exception where T : new()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
_AssertFail<T2>(() => xml.DeserializeString<T>(xmlString));
|
||||
var deserializer = new CXmlDeserializer();
|
||||
_AssertFail<T2>(() => deserializer.DeserializeString<T>(xmlString));
|
||||
}
|
||||
|
||||
private static T _AssertSerDeserMatch<T>(string xmlString) where T : new()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
T foo = xml.DeserializeString<T>(xmlString);
|
||||
string xmlNew = xml.Serialize(foo);
|
||||
var deserializer = new CXmlDeserializer();
|
||||
var serializer = new CXmlSerializer();
|
||||
T foo = deserializer.DeserializeString<T>(xmlString);
|
||||
string xmlNew = serializer.Serialize(foo);
|
||||
Assert.AreEqual(xmlString, xmlNew);
|
||||
return foo;
|
||||
}
|
||||
@@ -134,7 +135,7 @@ namespace VocaluxeTests
|
||||
<F>3</F>
|
||||
<S>2</S>
|
||||
</root>";
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
SBasic foo = xml.DeserializeString<SBasic>(s);
|
||||
Assert.AreEqual(1, foo.I);
|
||||
Assert.AreEqual("2", foo.S);
|
||||
@@ -235,20 +236,22 @@ namespace VocaluxeTests
|
||||
[TestMethod]
|
||||
public void TestList()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
var ser = new CXmlSerializer();
|
||||
SList foo = xml.DeserializeString<SList>(_XMLList[0]);
|
||||
Assert.AreEqual(foo.Ints.Count, 2, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[0].I, 1, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[1].I, 1, "Deserialization failed");
|
||||
string res = xml.Serialize(foo);
|
||||
string res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLList[0], res, "Serialization failed");
|
||||
foo = xml.DeserializeString<SList>(_XMLList[1]);
|
||||
Assert.AreEqual(foo.Ints.Count, 0, "Deserialization2 failed");
|
||||
res = xml.Serialize(foo);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLList[2], res, "Serialization2 failed");
|
||||
foo = xml.DeserializeString<SList>(_XMLList[2]);
|
||||
Assert.AreEqual(foo.Ints.Count, 0, "Deserialization2 failed");
|
||||
res = xml.Serialize(foo);
|
||||
_AssertSerDeserMatch<SList>(_XMLList[2]);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLList[2], res, "Serialization2 failed");
|
||||
_AssertFail<SList, XmlException>(_Empty);
|
||||
}
|
||||
@@ -256,20 +259,21 @@ namespace VocaluxeTests
|
||||
[TestMethod]
|
||||
public void TestArray()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
var ser = new CXmlSerializer();
|
||||
SArray foo = xml.DeserializeString<SArray>(_XMLList[0]);
|
||||
Assert.AreEqual(foo.Ints.Length, 2, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[0].I, 1, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[1].I, 1, "Deserialization failed");
|
||||
string res = xml.Serialize(foo);
|
||||
string res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLList[0], res, "Serialization failed");
|
||||
foo = xml.DeserializeString<SArray>(_XMLList[1]);
|
||||
Assert.AreEqual(foo.Ints.Length, 0, "Deserialization2 failed");
|
||||
res = xml.Serialize(foo);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLList[2], res, "Serialization2 failed");
|
||||
foo = xml.DeserializeString<SArray>(_XMLList[2]);
|
||||
Assert.AreEqual(foo.Ints.Length, 0, "Deserialization2 failed");
|
||||
res = xml.Serialize(foo);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLList[2], res, "Serialization2 failed");
|
||||
_AssertFail<SArray, XmlException>(_Empty);
|
||||
}
|
||||
@@ -299,20 +303,21 @@ namespace VocaluxeTests
|
||||
[TestMethod]
|
||||
public void TestListEmbedded()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
var ser = new CXmlSerializer();
|
||||
SListEmb foo = xml.DeserializeString<SListEmb>(_XMLListEmb);
|
||||
Assert.AreEqual(foo.Ints.Count, 1, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[0].I, 1, "Deserialization failed");
|
||||
string res = xml.Serialize(foo);
|
||||
string res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLListEmb, res, "Serialization failed");
|
||||
foo = xml.DeserializeString<SListEmb>(_XMLListEmb2);
|
||||
Assert.AreEqual(foo.Ints.Count, 2, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[1].I, 2, "Deserialization failed");
|
||||
res = xml.Serialize(foo);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLListEmb2, res, "Serialization failed");
|
||||
foo = xml.DeserializeString<SListEmb>(_XMLListEmb3);
|
||||
Assert.AreEqual(foo.Ints.Count, 0, "Deserialization failed");
|
||||
res = xml.Serialize(foo);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLListEmb3, res, "Serialization failed");
|
||||
_AssertFail<SListEmb, XmlException>(_Empty);
|
||||
}
|
||||
@@ -320,20 +325,21 @@ namespace VocaluxeTests
|
||||
[TestMethod]
|
||||
public void TestArrayEmbedded()
|
||||
{
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
var ser = new CXmlSerializer();
|
||||
SArrayEmb foo = xml.DeserializeString<SArrayEmb>(_XMLListEmb);
|
||||
Assert.AreEqual(foo.Ints.Length, 1, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[0].I, 1, "Deserialization failed");
|
||||
string res = xml.Serialize(foo);
|
||||
string res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLListEmb, res, "Serialization failed");
|
||||
foo = xml.DeserializeString<SArrayEmb>(_XMLListEmb2);
|
||||
Assert.AreEqual(foo.Ints.Length, 2, "Deserialization failed");
|
||||
Assert.AreEqual(foo.Ints[1].I, 2, "Deserialization failed");
|
||||
res = xml.Serialize(foo);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLListEmb2, res, "Serialization failed");
|
||||
foo = xml.DeserializeString<SArrayEmb>(_XMLListEmb3);
|
||||
Assert.AreEqual(foo.Ints.Length, 0, "Deserialization failed");
|
||||
res = xml.Serialize(foo);
|
||||
res = ser.Serialize(foo);
|
||||
Assert.AreEqual(_XMLListEmb3, res, "Serialization failed");
|
||||
_AssertFail<SArrayEmb, XmlException>(_Empty);
|
||||
}
|
||||
@@ -345,12 +351,9 @@ namespace VocaluxeTests
|
||||
<Public>2</Public>
|
||||
<Auto>3</Auto>
|
||||
</root>";
|
||||
var xml = new CXmlSerializer();
|
||||
SProperty foo = xml.DeserializeString<SProperty>(xmlString);
|
||||
SProperty foo = _AssertSerDeserMatch<SProperty>(xmlString);
|
||||
Assert.AreEqual(3, foo.Private);
|
||||
Assert.AreEqual(3, foo.Auto);
|
||||
string newXml = xml.Serialize(foo);
|
||||
Assert.AreEqual(xmlString, newXml);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -381,16 +384,17 @@ namespace VocaluxeTests
|
||||
public void TestExisting()
|
||||
{
|
||||
var foo = new SIgnore {I = 1};
|
||||
var xml = new CXmlSerializer();
|
||||
var xml = new CXmlDeserializer();
|
||||
var ser = new CXmlSerializer();
|
||||
SIgnore bar = xml.DeserializeString(_XmlIgnore, foo);
|
||||
Assert.AreEqual(1, bar.I);
|
||||
Assert.AreEqual(_XmlIgnore, xml.Serialize(bar));
|
||||
Assert.AreEqual(_XmlIgnore, ser.Serialize(bar));
|
||||
|
||||
var foo2 = new CIgnore {I = 1};
|
||||
CIgnore bar2 = xml.DeserializeString(_XmlIgnore, foo2);
|
||||
Assert.AreEqual(1, bar2.I);
|
||||
Assert.AreEqual(foo2.J, bar2.J, "Original classes should be modified by the deserialization");
|
||||
Assert.AreEqual(_XmlIgnore, xml.Serialize(bar2));
|
||||
Assert.AreEqual(_XmlIgnore, ser.Serialize(bar2));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -421,18 +425,19 @@ namespace VocaluxeTests
|
||||
foreach (Type type in types)
|
||||
{
|
||||
string xmlPath = Path.Combine(filePath, type.Name + ".xml");
|
||||
var xml = new CXmlSerializer(type == typeof(CConfig.SConfig));
|
||||
var deser = new CXmlDeserializer();
|
||||
object foo = null;
|
||||
try
|
||||
{
|
||||
foo = xml.Deserialize(xmlPath, Activator.CreateInstance(type));
|
||||
foo = deser.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);
|
||||
var ser = new CXmlSerializer(type == typeof(CConfig.SConfig));
|
||||
string newXml = ser.Serialize(foo);
|
||||
string oldXml = File.ReadAllText(xmlPath);
|
||||
Assert.AreEqual(oldXml, newXml, "Error with " + type.Name);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user