925 lines
30 KiB
C#
925 lines
30 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Xml;
|
|
using JD.Kernel.Classes;
|
|
|
|
namespace JD.Serializers.Xml
|
|
{
|
|
/// <summary>
|
|
/// Class used to help reading xml files.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is the fastest XmlSerializer for reading Xml files and it has a very low
|
|
/// memory overhead. It has some drawbacks though:
|
|
/// <list type="bullet">
|
|
/// <item><description>
|
|
/// You cannot read attributes of an element after you've started reading its sub-elements,
|
|
/// meaning you have to read all the needed attributes of a level before going on further.
|
|
/// </description></item>
|
|
/// <item><description>
|
|
/// Being a forward-only serializer, it provides no enumerating capabilities.
|
|
/// </description></item>
|
|
/// <item><description>
|
|
/// Elements must be read in exactly the same order they were written.
|
|
/// </description></item>
|
|
/// </list>
|
|
/// </remarks>
|
|
public sealed class XmlSerializerReader : XmlSerializer, IEnumerable<string>
|
|
{
|
|
#region Inner Classes
|
|
|
|
private class ElementReader : IXmlElement
|
|
{
|
|
#region Variables
|
|
|
|
private readonly XmlSerializerReader m_reader;
|
|
private readonly string m_elementName;
|
|
private readonly int m_depth;
|
|
private readonly bool m_needToClose;
|
|
|
|
private bool m_disposed;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Here we assume the reader is positioned on a start element tag.
|
|
/// </summary>
|
|
/// <param name="reader"></param>
|
|
public ElementReader(XmlSerializerReader reader)
|
|
{
|
|
System.Diagnostics.Debug.Assert(reader != null);
|
|
m_reader = reader;
|
|
m_elementName = m_reader.m_reader.LocalName;
|
|
m_depth = m_reader.m_currentDepth;
|
|
m_needToClose = !m_reader.IsEmptyElement;
|
|
|
|
++m_reader.m_currentDepth;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (!m_disposed)
|
|
{
|
|
if (m_needToClose)
|
|
{
|
|
m_reader.EndElement(m_elementName, m_depth);
|
|
}
|
|
|
|
m_reader.m_nextElement = null;
|
|
|
|
m_reader.m_currentDepth = m_depth;
|
|
m_disposed = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public string Name
|
|
{
|
|
get
|
|
{
|
|
return m_elementName;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
private class ElementEnumerator : IEnumerator<string>
|
|
{
|
|
#region Variables
|
|
|
|
protected readonly XmlSerializerReader m_reader;
|
|
private IXmlElement m_currentElement;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public ElementEnumerator(XmlSerializerReader reader)
|
|
{
|
|
System.Diagnostics.Debug.Assert(reader != null);
|
|
m_reader = reader;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (m_currentElement != null)
|
|
{
|
|
m_currentElement.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public string Current
|
|
{
|
|
get
|
|
{
|
|
return m_currentElement.Name;
|
|
}
|
|
}
|
|
|
|
object System.Collections.IEnumerator.Current
|
|
{
|
|
get
|
|
{
|
|
return Current;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
public bool MoveNext()
|
|
{
|
|
Dispose();
|
|
m_currentElement = GetNextElement();
|
|
return m_currentElement != null;
|
|
}
|
|
|
|
protected virtual IXmlElement GetNextElement()
|
|
{
|
|
return m_reader.StartElement();
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
throw new InvalidOperationException("Cannot reset an xml enumerator.");
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
private class NamedElementEnumerator : ElementEnumerator, IEnumerable<string>
|
|
{
|
|
#region Variables
|
|
|
|
private readonly string m_name;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public NamedElementEnumerator(XmlSerializerReader reader, string name)
|
|
: base(reader)
|
|
{
|
|
m_name = name;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
protected override IXmlElement GetNextElement()
|
|
{
|
|
return m_reader.StartElement(m_name, false, false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IEnumerable<string> Members
|
|
|
|
public IEnumerator<string> GetEnumerator()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
private readonly XmlReader m_reader;
|
|
private readonly Version m_version;
|
|
private readonly IXmlLineInfo m_linePositionProvider;
|
|
|
|
private int m_currentDepth;
|
|
|
|
/// <summary>
|
|
/// The next element is the next element that is already open (used with NextElementIs)
|
|
/// </summary>
|
|
private IXmlElement m_nextElement;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// XmlSerializerReader Constructor
|
|
/// </summary>
|
|
/// <param name="stream">Xml Stream to read</param>
|
|
public XmlSerializerReader(System.IO.Stream stream)
|
|
: this(stream, null)
|
|
{ }
|
|
|
|
/// <summary>
|
|
/// Constructor with a fallback filename.
|
|
/// </summary>
|
|
/// <param name="stream"></param>
|
|
/// <param name="fallbackFilename"></param>
|
|
public XmlSerializerReader(System.IO.Stream stream, string fallbackFilename)
|
|
: base(stream, fallbackFilename)
|
|
{
|
|
using (SafeHandle<System.IO.Stream> streamHandle = stream)
|
|
{
|
|
// For now we are reading too much content with this bufferedstream, renable we will split content and definition
|
|
//stream = new BufferedStream(stream, 64 * 1024);
|
|
|
|
m_reader = XmlReader.Create(stream, CreateDefaultSettings());
|
|
m_linePositionProvider = (IXmlLineInfo)m_reader;
|
|
|
|
// Read first element with version
|
|
m_reader.Read();
|
|
|
|
if (m_reader.NodeType == XmlNodeType.XmlDeclaration)
|
|
{
|
|
m_reader.Read();
|
|
}
|
|
|
|
string version = "0.0";
|
|
if (m_reader.LocalName == RootElement ||
|
|
// Backward compatibility!
|
|
m_reader.LocalName == "Mosaic")
|
|
{
|
|
string serializedVersion = Serialize(VersionAttribute);
|
|
if (!string.IsNullOrEmpty(serializedVersion))
|
|
{
|
|
version = serializedVersion;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_nextElement = new ElementReader(this);
|
|
}
|
|
m_version = new Version(version);
|
|
|
|
streamHandle.Release();
|
|
}
|
|
}
|
|
|
|
private static XmlReaderSettings CreateDefaultSettings()
|
|
{
|
|
XmlReaderSettings settings = new XmlReaderSettings
|
|
{
|
|
IgnoreComments = true,
|
|
IgnoreWhitespace = true,
|
|
CloseInput = true
|
|
};
|
|
|
|
return settings;
|
|
}
|
|
|
|
/// <summary>
|
|
/// XmlSerializerReader Constructor
|
|
/// </summary>
|
|
/// <param name="filename">File to read from</param>
|
|
public XmlSerializerReader(string filename)
|
|
: this(File.OpenRead(filename))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Overriden.
|
|
/// </summary>
|
|
/// <param name="disposing"></param>
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
if (m_reader != null)
|
|
{
|
|
m_reader.Close();
|
|
}
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Gets the number of attributes on the current node.
|
|
/// </summary>
|
|
public int AttributeCount
|
|
{
|
|
get
|
|
{
|
|
return m_reader.AttributeCount;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the current element is an empty xml node (<node />).
|
|
/// </summary>
|
|
public bool IsEmptyElement
|
|
{
|
|
get
|
|
{
|
|
m_reader.MoveToElement();
|
|
return m_reader.IsEmptyElement;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Current line number.
|
|
/// </summary>
|
|
public int LineNumber
|
|
{
|
|
get
|
|
{
|
|
return m_linePositionProvider.LineNumber;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Current column number.
|
|
/// </summary>
|
|
public int ColumnNumber
|
|
{
|
|
get
|
|
{
|
|
return m_linePositionProvider.LinePosition;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
#region General
|
|
|
|
/// <summary>
|
|
/// Starts a new element.
|
|
/// </summary>
|
|
/// <param name="element">Name of the element to start.</param>
|
|
/// <returns>The started element.</returns>
|
|
/// <exception cref="XmlException">Thrown if no element of that name if found on the current level.</exception>
|
|
public override IXmlElement StartElement(string element)
|
|
{
|
|
return StartElement(element, true, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts a new element.
|
|
/// </summary>
|
|
/// <param name="element">Name of the element to start.</param>
|
|
/// <param name="skipBadElements">Whether to skip elements which don't have the given name.</param>
|
|
/// <returns>The started element.</returns>
|
|
/// <exception cref="XmlException">Thrown if no element of that name if found on the current level.</exception>
|
|
public IXmlElement StartElement(string element, bool skipBadElements)
|
|
{
|
|
return StartElement(element, skipBadElements, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts a new element.
|
|
/// </summary>
|
|
/// <param name="element">Name of the element to start.</param>
|
|
/// <param name="skipBadElements">Whether to skip elements which don't have the given name.</param>
|
|
/// <param name="throwIfNotFound">Whether to throw an exception if the element is not found on the current level.</param>
|
|
/// <returns>The started element, or null if not found.</returns>
|
|
/// <exception cref="XmlException">Thrown if no element of that name if found on the current level and <paramref name="throwIfNotFound"/> is true.</exception>
|
|
public IXmlElement StartElement(string element, bool skipBadElements, bool throwIfNotFound)
|
|
{
|
|
while (true)
|
|
{
|
|
IXmlElement xmlElement = StartElement();
|
|
{
|
|
if (xmlElement == null && throwIfNotFound)
|
|
{
|
|
throw new XmlException("Element <" + element + "> not found.");
|
|
}
|
|
|
|
if (xmlElement == null || xmlElement.Name == element)
|
|
{
|
|
m_nextElement = null;
|
|
return xmlElement;
|
|
}
|
|
|
|
if (!skipBadElements)
|
|
{
|
|
m_nextElement = xmlElement;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
DisposableHelper.SafeDispose(xmlElement);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts the next available element.
|
|
/// </summary>
|
|
/// <returns>The started element, or null if not found.</returns>
|
|
public IXmlElement StartElement()
|
|
{
|
|
IXmlElement element = null;
|
|
|
|
// The node has already been started
|
|
if (m_nextElement != null)
|
|
{
|
|
element = m_nextElement;
|
|
m_nextElement = null;
|
|
return element;
|
|
}
|
|
|
|
Advance(delegate
|
|
{
|
|
if (m_reader.NodeType == XmlNodeType.Element && m_reader.Depth == m_currentDepth + 1)
|
|
{
|
|
// We found an element.
|
|
element = new ElementReader(this);
|
|
return false;
|
|
}
|
|
|
|
if (m_reader.NodeType == XmlNodeType.EndElement && m_reader.Depth == m_currentDepth)
|
|
{
|
|
// No sub elements found.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
return element;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads xml inner data.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public string ReadInnerData()
|
|
{
|
|
string data = null;
|
|
|
|
Advance(delegate
|
|
{
|
|
if ((m_reader.NodeType == XmlNodeType.Text || m_reader.NodeType == XmlNodeType.CDATA) && m_reader.Depth == m_currentDepth + 1)
|
|
{
|
|
data = m_reader.Value;
|
|
}
|
|
else if (m_reader.NodeType == XmlNodeType.EndElement && m_reader.Depth == m_currentDepth)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
return StringRecycler.Instance.Intern(data);
|
|
}
|
|
|
|
private bool AdvanceToStartElement()
|
|
{
|
|
m_reader.MoveToElement();
|
|
int depth = m_reader.Depth;
|
|
if (depth == m_currentDepth && m_reader.IsEmptyElement)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ((depth == m_currentDepth && m_reader.NodeType == XmlNodeType.Element) ||
|
|
(depth == m_currentDepth + 1 && (m_reader.NodeType == XmlNodeType.EndElement || m_reader.IsEmptyElement)))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private delegate bool AdvanceDelegate();
|
|
|
|
private void Advance(AdvanceDelegate action)
|
|
{
|
|
System.Diagnostics.Debug.Assert(action != null);
|
|
|
|
if (AdvanceToStartElement())
|
|
{
|
|
while (m_reader.Read())
|
|
{
|
|
if (!action())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks whether the next element is <paramref name="element"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// You cannot read the attributes of the current node after calling this method.
|
|
/// </remarks>
|
|
/// <param name="element">Name of the element to test for.</param>
|
|
/// <returns>True if the name of the next element is <paramref name="element"/>.</returns>
|
|
public bool NextElementIs(string element)
|
|
{
|
|
IXmlElement xmlElement = m_nextElement ?? StartElement();
|
|
if (xmlElement == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_nextElement = xmlElement;
|
|
return (m_nextElement.Name == element);
|
|
}
|
|
|
|
private void EndElement(string elementName, int depth)
|
|
{
|
|
do
|
|
{
|
|
if (m_reader.NodeType == XmlNodeType.EndElement && m_reader.LocalName == elementName && m_reader.Depth == depth + 1)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
while (m_reader.Read());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetEnumerator
|
|
|
|
/// <summary>
|
|
/// Returns an <see cref="IEnumerable{String}"/> to iterate through the elements of the current level with the given <paramref name="element"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Iteration stops when encountering an element with a different name, even on the same level.
|
|
/// </remarks>
|
|
/// <param name="element">Name of the elements to iterate through.</param>
|
|
/// <returns></returns>
|
|
public IEnumerable<string> GetEnumerator(string element)
|
|
{
|
|
return new NamedElementEnumerator(this, element);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an <see cref="IEnumerable{String}"/> to iterate through the elements of the current level.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerator<string> GetEnumerator()
|
|
{
|
|
return new ElementEnumerator(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns an <see cref="IEnumerable{String}"/> to iterate through the elements of the current level.
|
|
/// </summary> ///
|
|
/// <returns></returns>
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Serialize
|
|
|
|
/// <summary>
|
|
/// Serializes a string value.
|
|
/// </summary>
|
|
/// <param name="attribute">Name of the attribute to serialize</param>
|
|
/// <returns>Value of the attribute</returns>
|
|
public string Serialize(string attribute)
|
|
{
|
|
if (!m_reader.MoveToAttribute(attribute))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return SerializeStringImpl();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes any basic type.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to serialize</typeparam>
|
|
/// <param name="attribute">Name of the attribute to serialize</param>
|
|
/// <returns>Value of the attribute</returns>
|
|
public T Serialize<T>(string attribute)
|
|
{
|
|
return Serialize(attribute, default(T));
|
|
}
|
|
|
|
public T Serialize<T>(string attribute, T defaultValue)
|
|
{
|
|
if (!m_reader.MoveToAttribute(attribute))
|
|
{
|
|
return defaultValue;
|
|
}
|
|
|
|
try
|
|
{
|
|
return SerializeImpl<T>();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new XmlException("Error serializing attribute '" + attribute + "': " + ex.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes any type, using the given <paramref name="type"/> to convert the value from a string.
|
|
/// </summary>
|
|
/// <param name="attribute">Name of the attribute to serialize</param>
|
|
/// <param name="type">The converter to use to convert the attribute value from a string</param>
|
|
/// <returns>Value of the attribute</returns>
|
|
public object Serialize(string attribute, Type type)
|
|
{
|
|
return Serialize<object>(attribute, GetConverter(type));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes any type, using the given <paramref name="converter"/> to convert the value from a string.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to serialize</typeparam>
|
|
/// <param name="attribute">Name of the attribute to serialize</param>
|
|
/// <param name="converter">The converter to use to convert the attribute value from a string</param>
|
|
/// <returns>Value of the attribute</returns>
|
|
public T Serialize<T>(string attribute, TypeConverter converter)
|
|
{
|
|
if (!m_reader.MoveToAttribute(attribute))
|
|
{
|
|
return default(T);
|
|
}
|
|
|
|
try
|
|
{
|
|
return SerializeImpl<T>(converter);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new XmlException("Error serializing attribute '" + attribute + "': " + ex.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes an array made of any basic type.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value the array contains</typeparam>
|
|
/// <param name="attribute">Name of the attribute to serialize</param>
|
|
/// <returns>The serialized array</returns>
|
|
public T[] SerializeArray<T>(string attribute)
|
|
{
|
|
return SerializeArray<T>(attribute, DefaultArraySeparator);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes an array made of any basic type.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value the array contains</typeparam>
|
|
/// <param name="attribute">Name of the attribute to serialize</param>
|
|
/// <param name="separator">Element separator.</param>
|
|
/// <returns>The serialized array</returns>
|
|
public T[] SerializeArray<T>(string attribute, char separator)
|
|
{
|
|
if (!m_reader.MoveToAttribute(attribute))
|
|
{
|
|
return new T[0];
|
|
}
|
|
|
|
return SerializeArrayImpl<T>(separator);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes a string value.
|
|
/// </summary>
|
|
/// <param name="attributeIndex">Index of the attribute to serialize</param>
|
|
/// <returns>Value of the attribute</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="attributeIndex"/> is negative or larger than <see cref="AttributeCount"/>.</exception>
|
|
public string Serialize(int attributeIndex)
|
|
{
|
|
if (attributeIndex < 0 || attributeIndex >= m_reader.AttributeCount)
|
|
{
|
|
throw new ArgumentOutOfRangeException("attributeIndex");
|
|
}
|
|
|
|
m_reader.MoveToAttribute(attributeIndex);
|
|
|
|
return SerializeStringImpl();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes any basic type from a text node. (Using its type converter)
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to serialize</typeparam>
|
|
/// <returns>Value of the node</returns>
|
|
public T Serialize<T>()
|
|
{
|
|
return (T)(Serialize(typeof(T)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes any basic type from a text node. (Using its type converter)
|
|
/// </summary>
|
|
/// <param name="expectedType">The type of value to serialize</param>
|
|
/// <returns>Value of the node</returns>
|
|
public object Serialize(Type expectedType)
|
|
{
|
|
try
|
|
{
|
|
string innerData = ReadInnerData();
|
|
if (innerData == null)
|
|
{
|
|
return expectedType.IsValueType ? Activator.CreateInstance(expectedType) : null;
|
|
}
|
|
|
|
char whiteSpace;
|
|
if (expectedType == typeof(char) && XmlWhiteSpaceEncoder.TryParse(innerData, out whiteSpace))
|
|
{
|
|
return whiteSpace;
|
|
}
|
|
|
|
return SerializeImpl(innerData, expectedType);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
string nextElementName = m_nextElement != null ? m_nextElement.Name : "[None]";
|
|
throw new XmlException("Error serializing value node '" + nextElementName + "': " + ex.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes any basic type.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value to serialize</typeparam>
|
|
/// <param name="attributeIndex">Index of the attribute to serialize</param>
|
|
/// <returns>Value of the attribute</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="attributeIndex"/> is negative or larger than <see cref="AttributeCount"/>.</exception>
|
|
public T Serialize<T>(int attributeIndex)
|
|
{
|
|
if (attributeIndex < 0 || attributeIndex >= m_reader.AttributeCount)
|
|
{
|
|
throw new ArgumentOutOfRangeException("attributeIndex");
|
|
}
|
|
|
|
m_reader.MoveToAttribute(attributeIndex);
|
|
|
|
try
|
|
{
|
|
return SerializeImpl<T>();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new XmlException("Error serializing attribute number '" + attributeIndex + "': " + ex.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes an array made of any basic type.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value the array contains</typeparam>
|
|
/// <param name="attributeIndex">Index of the attribute to serialize</param>
|
|
/// <returns>The serialized array</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="attributeIndex"/> is negative or larger than <see cref="AttributeCount"/>.</exception>
|
|
public T[] SerializeArray<T>(int attributeIndex)
|
|
{
|
|
return SerializeArray<T>(attributeIndex, DefaultArraySeparator);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes an array made of any basic type.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of value the array contains</typeparam>
|
|
/// <param name="attributeIndex">Index of the attribute to serialize</param>
|
|
/// <param name="separator">Element separator.</param>
|
|
/// <returns>The serialized array</returns>
|
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="attributeIndex"/> is negative or larger than <see cref="AttributeCount"/>.</exception>
|
|
public T[] SerializeArray<T>(int attributeIndex, char separator)
|
|
{
|
|
if (attributeIndex < 0 || attributeIndex >= m_reader.AttributeCount)
|
|
{
|
|
throw new ArgumentOutOfRangeException("attributeIndex");
|
|
}
|
|
|
|
m_reader.MoveToAttribute(attributeIndex);
|
|
|
|
return SerializeArrayImpl<T>(separator);
|
|
}
|
|
|
|
private string SerializeStringImpl()
|
|
{
|
|
return StringRecycler.Instance.Intern(m_reader.Value);
|
|
}
|
|
|
|
private T SerializeImpl<T>()
|
|
{
|
|
return SerializeImpl<T>(m_reader.Value);
|
|
}
|
|
|
|
private T SerializeImpl<T>(TypeConverter converter)
|
|
{
|
|
return SerializeImpl<T>(m_reader.Value, converter);
|
|
}
|
|
|
|
private T SerializeImpl<T>(string invariantString)
|
|
{
|
|
return (T)SerializeImpl(invariantString, typeof(T));
|
|
}
|
|
|
|
private static T SerializeImpl<T>(string invariantString, TypeConverter converter)
|
|
{
|
|
return (T)SerializeImpl(invariantString, converter);
|
|
}
|
|
|
|
private object SerializeImpl(string invariantString, Type expectedType)
|
|
{
|
|
return SerializeImpl(invariantString, GetConverter(expectedType));
|
|
}
|
|
|
|
private static object SerializeImpl(string invariantString, TypeConverter converter)
|
|
{
|
|
var result = converter.ConvertFromInvariantString(invariantString);
|
|
|
|
if (result is string)
|
|
{
|
|
result = StringRecycler.Instance.Intern((string)result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private T[] SerializeArrayImpl<T>(char separator)
|
|
{
|
|
string[] splittedValue = m_reader.Value.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
Type type = typeof(T);
|
|
Array arr = Array.CreateInstance(type, splittedValue.Length);
|
|
TypeConverter c = GetConverter(type);
|
|
for (int i = 0; i < arr.Length; ++i)
|
|
{
|
|
if (!string.IsNullOrEmpty(XmlConvert.DecodeName(splittedValue[i])))
|
|
{
|
|
arr.SetValue(c.ConvertFromInvariantString(splittedValue[i]), i);
|
|
}
|
|
}
|
|
|
|
return (T[])arr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes any object type.
|
|
/// </summary>
|
|
/// <param name="attribute">Name of the attribute to serialize.</param>
|
|
/// <returns>The serialized object</returns>
|
|
public object SerializeObject(string attribute)
|
|
{
|
|
byte[] data = Convert.FromBase64String(Serialize(attribute));
|
|
|
|
using (MemoryStream stream = new MemoryStream(data))
|
|
{
|
|
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
|
|
return formatter.Deserialize(stream);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Attributes
|
|
|
|
/// <summary>
|
|
/// Gets all the attributes in a key-value pair form on the current node.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public IEnumerable<KeyValuePair<string, string>> GetAttributes()
|
|
{
|
|
for (int i = 0; i < m_reader.AttributeCount; i++)
|
|
{
|
|
m_reader.MoveToAttribute(i);
|
|
yield return new KeyValuePair<string, string>(m_reader.Name, m_reader.Value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies if the attribute exists
|
|
/// </summary>
|
|
/// <param name="attribute">name of the attribute</param>
|
|
/// <returns></returns>
|
|
public bool AttributeExists(string attribute)
|
|
{
|
|
return m_reader.MoveToAttribute(attribute);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
}
|