'C# generic serialization utility class
I have an existing class for serializing and deserializing objects to/from XML. It's a generic class with a single type parameter T whose only constraint is where T : IXmlSerializable. However, I want to still be able to use this class on classes that do not implement IXmlSerializable but have the [Serializable] attribute. How could I go about doing this?
From my generic class:
public static class XmlSerializationUtils<T> where T : IXmlSerializable
{
public static T DeserializeXml(XmlDocument xml) { ... }
public static XmlDocument SerializeToXml(T toSerialize) { ... }
}
I found this discussion but there was no solution given, just that I can't do where T : Serializable. Trying to do where T : SerializableAttribute makes Visual Studio say "Cannot use sealed class 'System.SerializableAttribute' as type parameter constraint".
Edit: based on Stephen's answer, I removed the constraints on XmlSerializationUtils<T> and added this static constructor:
static XmlSerializationUtils()
{
Type type = typeof(T);
bool hasAttribute = null != Attribute.GetCustomAttribute(type,
typeof(SerializableAttribute));
bool implementsInterface =
null != type.GetInterface(typeof(IXmlSerializable).FullName);
if (!hasAttribute && !implementsInterface)
{
throw new ArgumentException(
"Cannot use XmlSerializationUtils on class " + type.Name +
" because it does not have the Serializable attribute " +
" and it does not implement IXmlSerializable"
);
}
}
Solution 1:[1]
You can check to see if a type is serializable using the IsSerializable property of the Type of the object.
myObj.GetType().IsSerializable
As mentioned, this isn't possible to add as a generic constraint, but would most likely be checked in a constructor.
Solution 2:[2]
I'd just eliminate the type constraint and catch the SerializationException when the type does not serialize or deserialize properly... In fact, this allows your generic Serialize and Deserialize methods to accept a formatter
public enum Formatter { Binary, Xml }
that could control whether the serialization is binary or Xml
public class Serialization
{
public enum Formatter { Binary, Xml }
#region Serialization methods
public static void Serialize2File<T>(T obj, string pathSpec,
Formatter formatter)
{
try
{
switch (formatter)
{
case (Formatter.Binary):
using (var fs = new FileStream(pathSpec, FileMode.Create,
FileAccess.Write, FileShare.Write))
(new BinaryFormatter()).Serialize(fs, obj);
break;
case (Formatter.Xml):
var serializer = new XmlSerializer(typeof(T));
TextWriter textWriter = new StreamWriter(pathSpec);
serializer.Serialize(textWriter, obj);
textWriter.Close();
break;
default:
throw new MyCustomException("Invalid Formatter option");
}
}
catch (SerializationException sX)
{
var errMsg = String.Format(
"Unable to serialize {0} into file {1}",
obj, pathSpec);
throw new MyCustomException(errMsg, sX);
}
}
public static T DeSerializeFromFile<T>(string pathSpec,
Formatter formatter) where T : class
{
try
{
switch (formatter)
{
case (Formatter.Binary):
using (var strm = new FileStream(pathSpec,
FileMode.Open, FileAccess.Read))
{
IFormatter fmt = new BinaryFormatter();
var o = fmt.Deserialize(strm);
if (!(o is T))
throw new ArgumentException("Bad Data File");
return o as T;
}
case (Formatter.Xml):
var serializer = new XmlSerializer(typeof(T));
TextReader rdr = new StreamReader(pathSpec);
return (T)serializer.Deserialize(rdr);
default:
throw new MyCustomException("Invalid Formatter option");
}
}
catch (SerializationException sX)
{
var errMsg = String.Format(
"Unable to deserialize {0} from file {1}",
typeof(T), pathSpec);
throw new MyCustomException(errMsg, sX);
}
}
#endregion Serialization methods
}
Solution 3:[3]
C# generic serialization utility class.
The core of serializing classes that implement the IXmlSerializable or Serializable attribute is to check the given type before passing it to the serializer.
XmlDocument Serialize<T>(T obj)
{
Type type = typeof(T);
if (type.HasAttribute<SerializableAttribute>()
| type.ImplementsInterface<IXmlSerializableAttribute>())
return XmlSerializer<T>.Serialize(obj);
throw new InvalidOperationException("Unserializable object given.");
}
To do this, you should implement the following extension methods and a generic serializer.
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.IO;
using System.Text;
using System.Xml.Schema;
namespace System
{
public static partial class Extensions
{
/// <summary>
/// Checks if the object <see cref="Type"/> has the specified attribute.
/// </summary>
/// <param name="type">
/// Object <see cref="Type"/> for which you want to check whether
/// whether it has <see cref="Attribute"/> specified by the <typeparamref name="T"/>.
/// </param>
public static bool HasAtribute<T>(this Type type) where T : Attribute
{
return type.GetCustomAttributes(typeof(T), true).Any();
}
/// <summary>
/// Checks if the object <see cref="Type"/> implements the specified interface.
/// </summary>
/// <param name="type">
/// Object <see cref="Type"/> for which you want to check whether
/// whether it implements the interface specified by the <paramref name="interfaceType"/> parameter.
/// </param>
/// <param name="interfaceType">
/// The <see cref="Type"/> being tested, which is an interface.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="type"/> is not a class, value type, or interface,
/// and also if <paramref name="interfaceType"/> is not an interface.
/// </exception>
/// <exception cref="ArgumentException">
/// The value <see langword="null"/> was passed as one of the parameters.
/// </exception>
/// <returns>
/// <see langword="true"/> if the object <see cref="Type"/> implements the specified interface.
/// </returns>
public static bool ImplementsInterface(this Type type, Type interfaceType)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (interfaceType == null)
throw new ArgumentNullException(nameof(interfaceType));
if (!interfaceType.IsInterface)
throw new ArgumentException("Argument must be interface.",
nameof(interfaceType));
while (type != null)
{
Type[] interfaces = type.GetInterfaces();
{
int length = interfaces.Length;
for (int i = 0; i < length; i++)
{
if (interfaces[i] == interfaceType || interfaces[i].ImplementsInterface(interfaceType))
return true;
}
}
type = type.BaseType;
}
return false;
}
/// <summary>
/// Checks if the object <see cref="Type"/> implements the specified interface.
/// </summary>
/// <typeparam name="T">
/// The type being checked, which is an interface.
/// </typeparam>
/// <param name="type">
/// Object <see cref="Type"/> for which you want to check whether
/// whether it implements the specified interface <typeparamref name="T"/>.
/// </param>
/// <returns>
/// <see langword="true"/> if the object is <see cref="Type"/>
/// implements the <typeparamref name="T"/> interface.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="type"/> is not a class, value type, or interface.
/// </exception>
/// <exception cref="ArgumentException">
/// The value <see langword="null"/> was passed as <paramref name="type"/>.
/// </exception>
public static bool ImplementsInterface<T>(this Type type) where T : class =>
ImplementsInterface(type, typeof(T));
}
}
namespace System.Xml.Serialization
{
/// <summary>
/// Serializes and deserializes <typeparamref name="T"/> objects into XML documents.
/// Allows you to control the process of encoding objects in XML.
/// </summary>
/// <typeparam name="T">Object type.</typeparam>
public static class XmlSerializer<T>
{
private static readonly XmlSerializer _serializer = new XmlSerializer(typeof(T));
private static readonly XmlWriterSettings _defaultWriterSettings = new XmlWriterSettings
{
CheckCharacters = false,
CloseOutput = false,
ConformanceLevel = ConformanceLevel.Auto,
Encoding = DefaultEncoding,
Indent = true,
IndentChars = "\t",
NamespaceHandling = NamespaceHandling.OmitDuplicates,
NewLineChars = "\r\n",
NewLineHandling = NewLineHandling.Replace,
NewLineOnAttributes = false,
OmitXmlDeclaration = false
};
private static readonly XmlReaderSettings _defaultReaderSettings = new XmlReaderSettings
{
CheckCharacters = false,
CloseInput = false,
ConformanceLevel = ConformanceLevel.Auto,
DtdProcessing = DtdProcessing.Prohibit,
IgnoreComments = true,
IgnoreProcessingInstructions = true,
IgnoreWhitespace = true,
LineNumberOffset = 0,
LinePositionOffset = 0,
MaxCharactersFromEntities = 0,
MaxCharactersInDocument = 0,
NameTable = null,
ValidationFlags = XmlSchemaValidationFlags.None,
ValidationType = ValidationType.None,
XmlResolver = null
};
/// <summary>
/// Default character encoding.
/// </summary>
public static Encoding DefaultEncoding => Encoding.UTF8;
/// <summary>
/// Default settings for the <see cref="XmlWriter" /> instance being created.
/// </summary>
public static XmlWriterSettings DefaultXmlWriterSettings => _defaultWriterSettings.Clone();
/// <summary>
/// Default settings for the <see cref="XmlReader" /> instance that is created.
/// </summary>
public static XmlReaderSettings DefaultXmlReaderSettings => _defaultReaderSettings.Clone();
/// <summary>
/// Serializes the given object and returns an XML document.
/// </summary>
/// <param name="o">
/// An instance <typeparamref name="T"/> to serialize.
/// </param>
/// <param name="settings">
/// Settings for the new <see cref="XmlWriter" /> instance.
/// If <see langword="null"/> is specified,
/// settings are used <see cref="DefaultXmlWriterSettings"/>.
/// </param>
/// <returns>An instance of <see cref="XmlDocument"/> that represents given object.</returns>
public static XmlDocument Serialize(T o, XmlWriterSettings settings = null)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(sb, settings ?? DefaultXmlWriterSettings))
_serializer.Serialize(xmlWriter, o, (XmlSerializerNamespaces)null);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(sb.ToString());
return xmlDocument;
}
/// <summary>
/// Deserializes the object contained in the specified XML document.
/// </summary>
/// <param name="xmlDocument">
/// An XML document containing the serialized data.
/// </param>
/// <param name="settings">
/// Settings for the new <see cref="XmlReader" /> instance.
/// If <see langword="null"/> is specified,
/// settings are used <see cref="DefaultXmlReaderSettings"/>.
/// </param>
/// <returns> The deserialized object of type <typeparamref name="T"/>. </returns>
public static T Deserialize(XmlDocument xmlDocument, XmlReaderSettings settings)
{
string text = xmlDocument.OuterXml;
using (StringReader reader = new StringReader(text))
using (XmlReader xmlReader = XmlReader.Create(reader, DefaultXmlReaderSettings))
return (T)_serializer.Deserialize(xmlReader);
}
/// <summary>
/// Returns a value indicating whether this <see cref="XmlSerializer" /> can deserialize the specified XML document.
/// </summary>
/// <param name="xmlReader">
/// <see cref="XmlReader" /> Pointing to the document to deserialize.
/// </param>
/// <returns>
/// <see langword="true" /> If this <see cref="XmlSerializer" /> can deserialize an object, <see cref="XmlReader" /> indicates; otherwise, <see langword="false" />.
/// </returns>
public static bool CanDeserialize(XmlReader xmlReader)
{
return _serializer.CanDeserialize(xmlReader);
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | womp |
| Solution 2 | Charles Bretana |
| Solution 3 |
