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;
using System.Xml.Serialization;
namespace VocaluxeLib.Xml
{
public delegate string GetCommentDelegate(string name);
public class CXmlSerializer
{
private readonly GetCommentDelegate _GetCommentCallback;
private readonly bool _WriteDefaults;
public CXmlSerializer(bool writeDefaults = false, GetCommentDelegate getCommentCallback = null)
{
_WriteDefaults = writeDefaults;
_GetCommentCallback = getCommentCallback;
}
///
/// Uniform settings for writing XML files. ALWAYS use this!
///
private readonly XmlWriterSettings _XMLSettings = new XmlWriterSettings
{
Indent = true,
Encoding = Encoding.UTF8,
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().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)
{
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);
}
if (type.IsList() || type.IsArray)
{
Type subType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0];
String subName = arrayItemName ?? subType.GetTypeName();
List