C# XML serialization of derived classes
[XmlInclude(typeof(Square))]
public abstract class Shape {...}
(repeat for all known subtypes)
If the types are only known at runtime, you can supply them to the XmlSerializer
constructor, but: then it is important to cache and reuse that serializer instance; otherwise you will haemorrhage dynamically created assemblies. It does this automatically when you use the constructor that just takes a Type
, but not for the other overloads.
How to use XmlSerializer to serialize derived instances?
The problem you are seeing can be reproduced with the following minimal example:
public class BaseClass
{
}
public class DerivedClass : BaseClass, IXmlSerializable
{
#region IXmlSerializable Members
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader) { throw new NotImplementedException(); }
public void WriteXml(XmlWriter writer) { }
#endregion
}
Using the serialization code:
BaseClass baseClass = new DerivedClass();
using (var textWriter = new StringWriter())
{
using (var xmlWriter = XmlWriter.Create(textWriter))
{
var serializer = new XmlSerializer(typeof(BaseClass), new Type[] { typeof(DerivedClass) });
serializer.Serialize(xmlWriter, baseClass);
}
Console.WriteLine(textWriter.ToString());
}
The following exception is thrown (sample fiddle #1):
System.InvalidOperationException: There was an error generating the XML document.
---> System.InvalidOperationException: The type DerivedClass may not be used in this context. To use DerivedClass as a parameter, return type, or member of a class or struct, the parameter, return type, or member must be declared as type DerivedClass (it cannot be object). Objects of type DerivedClass may not be used in un-typed collections, such as ArrayLists.
This is among the most unhelpful exception messages I have seen from XmlSerializer
. To understand the exception, you need to understand how XmlSerializer
handles polymorphism via the [XmlInclude]
mechanism. If I remove IXmlSerializable
from DerivedClass
, the following XML is generated (fiddle #2):
<BaseClass xsi:type="DerivedClass" />
Notice the xsi:type
type attribute? That is a w3c standard attribute that XmlSerializer
uses to explicitly assert the type of a polymorphic element; it is documented here. When XmlSerializer
is deserializing a polymorphic type to which [XmlInclude]
attributes have been applied (either statically or through the constructor you are using), it will look for the xsi:type
attribute to determine the actual type to construct and deserialize.
It is this, apparently, which conflicts with IXmlSerializable
. A type which implements this interface should completely control its XML reading and writing. However, by parsing and interpreting the xsi:type
attribute, XmlSerializer
has already begun automatic deserialization, and so throws an exception due to the inconsistent deserialization strategies of the base and derived types.
What's more, adding IXmlSerializable
to the base type doesn't really fix the problem either If you do so, the xsi:type
attribute is never written, and later, when ReadXml()
is called, an object of the base type will get unconditionally constructed, as shown in fiddle #3.
(It's conceivable that Microsoft could have implemented a special case where XmlSerializer
begins automatic deserialization, then "backs off" and hands the task over to ReadXml()
when an IXmlSerializable
polymorphic type is encountered and constructed. But, they did not.)
The solution would seem to be to serialize your Filter
types automatically using the [XmlInclude]
mechanism. In fact I don't see any reason you need to use IXmlSerializable
, and was able to serialize your model successfully by removing IXmlSerializable
completely and making some minor changes to namespaces:
public static class XmlNamespaces
{
public const string OpengisWfs = "http://www.opengis.net/wfs";
}
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class Query
{
public Filter Filter { get; set; }
}
[XmlInclude(typeof(PropertyIsOpFilter))]
[XmlInclude(typeof(OpFilterBase))]
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class Filter
{
[XmlElement]
public Filter And { get; set; }
}
[XmlInclude(typeof(PropertyIsEqualToFilter))]
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class PropertyIsOpFilter : Filter
{
public Filter LeftOp { get; set; }
public Filter RightOp { get; set; }
}
[XmlRoot("IsEqualTo", Namespace = XmlNamespaces.OpengisWfs)]
public class PropertyIsEqualToFilter : PropertyIsOpFilter { }
[XmlInclude(typeof(LiteralFilter))]
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class OpFilterBase : Filter
{
public string Op { get; set; }
public object Value { get; set; }
}
[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
public class LiteralFilter : OpFilterBase { }
Notes:
For the
[XmlInclude]
mechanism to work, all the included types apparently must be in the same XML namespace as the base type. To ensure this I added[XmlRoot(Namespace = XmlNamespaces.OpengisWfs)]
to all theFilter
subtypes.The
[XmlInclude(typeof(DerivedType))]
attributes can be added either to their immediate parent type or to the lowest common base type. In the code above I added the attributes to the immediate parent types so that members of an intermediate type could be serialized successfully, e.g.:public class SomeClass
{
PropertyIsOpFilter IsOpFilter { get; set; }
}Consider marking intermediate types that cannot be instantiated as
abstract
, e.g.public abstract class Filter
. Consider marking types that are "most derived" assealed
, e.g.public sealed class LiteralFilter
If you use the
new XmlSerializer(Type, Type [])
constructor, you must statically cache the serializer to avoid a severe memory leak, as explained here. It's not necessary in my solution but you are using it in your question.
Sample fiddle #4 showing that the following XML is generated successfully:
<Query xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opengis.net/wfs">
<Filter xsi:type="PropertyIsEqualToFilter">
<LeftOp xsi:type="LiteralFilter">
<Value xsi:type="xsd:int">1</Value>
</LeftOp>
<RightOp xsi:type="LiteralFilter">
<Value xsi:type="xsd:int">1</Value>
</RightOp>
</Filter>
</Query>
How to use derived class for XML serialization?
Finally flipping figured most of this out.
I stepped back and started with a very simple working non-derived example and worked up to what I needed.
There were two things going on here. First the clashing type names, then the clashing property names. While I had bits of each of these right, the amount of permutations of options for structuring each when combined together had me confused.
To prevent the abstract and derived type names from clashing when serialized I needed to make the derived class type anonymous (here using the XmlType
attribute).
To stop the property names clashing I needed to ignore both the property in the derived class and the base class. To do this without editing the base class I was missing a vital piece, XmlAttributeOverrides
. I had seen this mentioned in the MSDN documentation for XmlSerializer.Serialize()
but the information there was pretty minimal in explaining what it pertained to. This answer to another question led me to David Woodward's excellent explanation.
I have yet to try any of this with a derived type list property, or with deserialization.
Below is complete basic example of a program that outputs a string with some serialized XML on the console output:
using System;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var TestBar = new MyXml.Bar()
{
Name = "John Smith",
};
Serializer s = new MyXml.Serializer();
var TestOutput = s.Serialize(TestBar);
Console.WriteLine(TestOutput);
}
}
public abstract class Bar
{
public abstract string Name { get; set; }
}
public abstract class Serializer
{
public abstract string Serialize(Bar bar);
}
namespace MyXml
{
public class Serializer : Test.Serializer
{
public override string Serialize(Test.Bar bar)
{
string Output = null;
var Stream = new MemoryStream();
var Encoding = new UTF8Encoding(false, true);
// Ignore the Name property in the *base* class!
var ao = new XmlAttributeOverrides();
var a = new XmlAttributes();
a.XmlElements.Clear(); // Clear any element attributes
a.XmlAttribute = null; // Remove any attribute attributes
a.XmlIgnore = true; // Set the ignore attribute value true
ao.Add(typeof(Test.Bar), "Name", a); // Set to use with Test.Bar.Name
using (var Writer = new XmlTextWriter(Stream, Encoding))
{
Writer.Formatting = Formatting.Indented;
var s = new XmlSerializer(typeof(Bar), ao);
s.Serialize(Writer, bar);
Output = Encoding.GetString(Stream.ToArray());
}
Stream.Dispose();
return Output;
}
}
[Serializable]
[XmlType(AnonymousType = true)] // Make type anonymous!
[XmlRoot(IsNullable = false)]
public class Bar : Test.Bar
{
[XmlIgnore] // Ignore the Name property in the *derived* class!
public override string Name
{
get => Unreverse(ReverseName);
set => ReverseName = Reverse(value);
}
[XmlElement("Name", IsNullable = false)]
public string ReverseName { get; set; }
private string Unreverse(string name)
{
return "John Smith"; // Smith, John -> John Smith
}
private string Reverse(string name)
{
return "Smith, John"; // John Smith -> Smith, John
}
}
}
}
Serialize a list of objects inheriting from class A to xml, so the names of the elements in the xml are B,C,D
Firstly DataContractSerializer
does not have a mechanism to support collection item polymorphism by changing collection element name(s). It only supports the known type mechanism which uses the i:type
attribute - which you indicate is not acceptable.
Since you are willing to switch to XmlSerializer
, you could use the attribute XmlArrayItemAttribute.Type
to specify element names for polymorphic types in lists:
public class AListObject
{
[XmlArrayItem(typeof(B))]
[XmlArrayItem(typeof(C))]
public List<A> SerializedListObjects { get; set; }
}
However, you also indicate that the polymorphic subtypes cannot be declared statically at compile type because they exist in some other assembly.
As a result, you will need to use the XmlAttributeOverrides
mechanism to specify all possible derived types for all List<A>
properties in runtime, and manually construct an XmlSerializer
using those overrides.
Here is a prototype solution. First, let's assume you have a root object that refers to an object containing a List<A>
like so:
public class RootObject
{
public AListObject AList { get; set; }
}
public class AListObject
{
public List<A> SerializedListObjects { get; set; }
}
(The root object could be the object with the List<A>
property, but doesn't need to be.) Let's also assume you know all such objects like AListObject
that may contain List<A>
properties.
With those assumptions, the following serializer factory can be used to generate an XmlSerializer
for any root object that may refer to any instances of the known types containing a List<A>
property:
public interface IXmlSerializerFactory
{
XmlSerializer CreateSerializer(Type rootType);
}
public static class AListSerializerFactory
{
static readonly XmlArrayItemTypeOverrideSerializerFactory instance;
static AListSerializerFactory()
{
// Include here a list of all types that have a List<A> property.
// You could use reflection to find all such public types in your assemblies.
var declaringTypeList = new []
{
typeof(AListObject),
};
// Include here a list of all base types with a corresponding mapping
// to find all derived types in runtime. Here you could use reflection
// to find all such types in your assemblies, as shown in
// https://stackoverflow.com/questions/857705/get-all-derived-types-of-a-type
var derivedTypesList = new Dictionary<Type, Func<IEnumerable<Type>>>
{
{ typeof(A), () => new [] { typeof(B), typeof(C) } },
};
instance = new XmlArrayItemTypeOverrideSerializerFactory(declaringTypeList, derivedTypesList);
}
public static IXmlSerializerFactory Instance { get { return instance; } }
}
public class XmlArrayItemTypeOverrideSerializerFactory : IXmlSerializerFactory
{
// To avoid a memory & resource leak, the serializers must be cached as explained in
// https://stackoverflow.com/questions/23897145/memory-leak-using-streamreader-and-xmlserializer
readonly object padlock = new object();
readonly Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>();
readonly XmlAttributeOverrides overrides;
public XmlArrayItemTypeOverrideSerializerFactory(IEnumerable<Type> declaringTypeList, IEnumerable<KeyValuePair<Type, Func<IEnumerable<Type>>>> derivedTypesList)
{
var completed = new HashSet<Type>();
overrides = declaringTypeList
.SelectMany(d => derivedTypesList.Select(p => new { declaringType = d, itemType = p.Key, derivedTypes = p.Value() }))
.Aggregate(new XmlAttributeOverrides(), (a, d) => a.AddXmlArrayItemTypes(d.declaringType, d.itemType, d.derivedTypes, completed));
}
public XmlSerializer CreateSerializer(Type rootType)
{
lock (padlock)
{
XmlSerializer serializer;
if (!serializers.TryGetValue(rootType, out serializer))
serializers[rootType] = serializer = new XmlSerializer(rootType, overrides);
return serializer;
}
}
}
public static partial class XmlAttributeOverridesExtensions
{
public static XmlAttributeOverrides AddXmlArrayItemTypes(this XmlAttributeOverrides overrides, Type declaringType, Type itemType, IEnumerable<Type> derivedTypes)
{
return overrides.AddXmlArrayItemTypes(declaringType, itemType, derivedTypes, new HashSet<Type>());
}
public static XmlAttributeOverrides AddXmlArrayItemTypes(this XmlAttributeOverrides overrides, Type declaringType, Type itemType, IEnumerable<Type> derivedTypes, HashSet<Type> completedTypes)
{
if (overrides == null || declaringType == null || itemType == null || derivedTypes == null || completedTypes == null)
throw new ArgumentNullException();
XmlAttributes attributes = null;
for (; declaringType != null && declaringType != typeof(object); declaringType = declaringType.BaseType)
{
// Avoid duplicate overrides.
if (!completedTypes.Add(declaringType))
break;
foreach (var property in declaringType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance))
{
// Skip the property if already ignored
if (property.IsDefined(typeof(XmlIgnoreAttribute), false))
continue;
// See if it is a list property, and if so, get its type.
var propertyItemType = property.PropertyType.GetListType();
if (propertyItemType == null)
continue;
// OK, its a List<itemType>. Add all the necessary XmlElementAttribute declarations.
if (propertyItemType == itemType)
{
if (attributes == null)
{
attributes = new XmlAttributes();
foreach (var derivedType in derivedTypes)
// Here we are assuming all the derived types have unique XML type names.
attributes.XmlArrayItems.Add(new XmlArrayItemAttribute { Type = derivedType });
if (itemType.IsConcreteType())
attributes.XmlArrayItems.Add(new XmlArrayItemAttribute { Type = itemType });
}
overrides.Add(declaringType, property.Name, attributes);
}
}
}
return overrides;
}
}
public static class TypeExtensions
{
public static bool IsConcreteType(this Type type)
{
return !type.IsAbstract && !type.IsInterface;
}
public static Type GetListType(this Type type)
{
while (type != null)
{
if (type.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(List<>))
return type.GetGenericArguments()[0];
}
type = type.BaseType;
}
return null;
}
}
Then, you can serialize and deserialize instances of RootObject
to and from XML as follows:
var root = new RootObject
{
AList = new AListObject
{
SerializedListObjects = new List<A> { new B(), new C() },
},
};
var serializer = AListSerializerFactory.Instance.CreateSerializer(root.GetType());
var xml = root.GetXml(serializer);
var root2 = xml.LoadFromXml<RootObject>(serializer);
Using the extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
{
serial = serial ?? new XmlSerializer(typeof(T));
using (var reader = new StringReader(xmlString))
{
return (T)serial.Deserialize(reader);
}
}
public static string GetXml<T>(this T obj, XmlSerializer serializer = null)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(serializer ?? new XmlSerializer(obj.GetType())).Serialize(xmlWriter, obj);
return textWriter.ToString();
}
}
}
And the result is:
<RootObject xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AList>
<SerializedListObjects>
<B />
<C />
</SerializedListObjects>
</AList>
</RootObject>
Notes:
As explained in Memory Leak using StreamReader and XmlSerializer, you must statically cache any
XmlSerializer
constructed withXmlAttributeOverrides
to avoid a severe memory leak. The documentation suggests using aHashtable
, howeverXmlAttributeOverrides
does not overrideEquals()
orGetHashCode()
, and does not provide enough access to its internal data for applications developers to write their own. Thus it's necessary to hand-craft some sort of static caching scheme wheneverXmlAttributeOverrides
is used.Given the complexity of finding and overriding the
XmlArrayItem
attributes of allList<A>
properties, you might consider sticking with the existingi:type
mechanism. It's simple, works well, is supported by bothDataContractSerializer
andXmlSerializer
, and is standard.I wrote the class
XmlArrayItemTypeOverrideSerializerFactory
in a generic way, which adds to the apparent complexity.
Working sample .Net fiddle here.
Using XmlSerializer to serialize derived classes
There are three ways of doing this; either you can use [XmlInclude]
against the type, or you can use XmlElement
/XmlArrayItem
against the property. They are all shown below; uncomment the pair you prefer:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
public class MyWrapper {
//2: [XmlElement("A", Type = typeof(ChildA))]
//2: [XmlElement("B", Type = typeof(ChildB))]
//3: [XmlArrayItem("A", Type = typeof(ChildA))]
//3: [XmlArrayItem("B", Type = typeof(ChildB))]
public List<ChildClass> Data { get; set; }
}
//1: [XmlInclude(typeof(ChildA))]
//1: [XmlInclude(typeof(ChildB))]
public abstract class ChildClass {
public string ChildProp { get; set; }
}
public class ChildA : ChildClass {
public string AProp { get; set; }
}
public class ChildB : ChildClass {
public string BProp { get; set; }
}
static class Program {
static void Main() {
var ser = new XmlSerializer(typeof(MyWrapper));
var obj = new MyWrapper {
Data = new List<ChildClass> {
new ChildA { ChildProp = "abc", AProp = "def"},
new ChildB { ChildProp = "ghi", BProp = "jkl"}}
};
ser.Serialize(Console.Out, obj);
}
}
Serialize/Deserialize derived class as base class
Your problem is that you are constructing your XmlSerializer
inconsistently during serialization and deserialization. You need to construct it using the same Type
argument in both cases, specifically the base type typeof(Device)
. Thus I'd suggest you replace your existing completely general serialization method with one specific for a Device
:
public static class DeviceExtensions
{
public static string SerializeDevice<TDevice>(this TDevice o) where TDevice : Device
{
// Ensure that [XmlInclude(typeof(TDevice))] is present on Device.
// (Included for clarity -- actually XmlSerializer will make a similar check.)
if (!typeof(Device).GetCustomAttributes<XmlIncludeAttribute>().Any(a => a.Type == o.GetType()))
{
throw new InvalidOperationException("Unknown device type " + o.GetType());
}
var serializer = new XmlSerializer(typeof(Device)); // Serialize as the base class
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
public static Device DeserializeDevice(this string xml)
{
var serial = new XmlSerializer(typeof(Device));
using (var reader = new StringReader(xml))
{
return (Device)serial.Deserialize(reader);
}
}
}
Then, apply [XmlInclude(typeof(TDevice))]
to Device
for all possible subtypes:
[XmlInclude(typeof(WindowsDevice))]
[XmlInclude(typeof(AndroidDevice))]
public abstract class Device
{
}
Then both types of devices can now be serialized and deserialized successfully while retaining their type, because XmlSerializer
will include an "xsi:type"
attribute to explicitly indicate the type:
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="WindowsDevice" />
Or
<Device xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="AndroidDevice" />
Sample fiddle.
Updates
So the issue was, that I serialized with typeof(WindowsDevice) instead of typeof(Device)?
Yes.
Any ideas for a solution which will work, if I have to use typeof(WindowsDevice)? Cause I have hundreds of classes and don't want to use hundreds of different XmlSerializer initializations...
This is more of an architectural question than a howto question. One possibility would be to introduce a custom attribute that you can apply to a class to indicate that any subtypes of that class should always be serialized as the attributed base type. All appropriate [XmlInclude(typeof(TDerivedType))]
attributes will also be required:
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class XmlBaseTypeAttribute : System.Attribute
{
}
[XmlInclude(typeof(WindowsDevice))]
[XmlInclude(typeof(AndroidDevice))]
[XmlBaseType]
public abstract class Device
{
}
Then modify your universal XML serialization code to look up the type hierarchy of the object being serialized for an [XmlBaseType]
attribute, and (de)serialize as that type:
public static class XmlExtensions
{
static Type GetSerializedType(this Type type)
{
var serializedType = type.BaseTypesAndSelf().Where(t => Attribute.IsDefined(t, typeof(XmlBaseTypeAttribute))).SingleOrDefault();
if (serializedType != null)
{
// Ensure that [XmlInclude(typeof(TDerived))] is present on the base type
// (Included for clarity -- actually XmlSerializer will make a similar check.)
if (!serializedType.GetCustomAttributes<XmlIncludeAttribute>().Any(a => a.Type == type))
{
throw new InvalidOperationException(string.Format("Unknown subtype {0} of type {1}", type, serializedType));
}
}
return serializedType ?? type;
}
public static string Serialize(this object o)
{
var serializer = new XmlSerializer(o.GetType().GetSerializedType());
using (var stringWriter = new StringWriterWithEncoding(Encoding.UTF8))
{
serializer.Serialize(stringWriter, o);
return stringWriter.ToString();
}
}
public static T Deserialize<T>(this string xml)
{
var serial = new XmlSerializer(typeof(T).GetSerializedType());
using (var reader = new StringReader(xml))
{
return (T)serial.Deserialize(reader);
}
}
}
Of course this means that if your code tries to deserialize XML it expects to contain a WindowsDevice
, it might actually get back an AndroidDevice
depending upon the contents of the XML.
Sample fiddle #2.
how to serialize a base class variable with a different name in a derived class
From your comment, it appears you are able to make changes to TheBaseClass
. Thus you can add a virtual bool ShouldSerialize{PropertyName}()
method for the BaseClassList
property in the base class and return true
. Then override it in the derived class and return false
, and introduce a proxy property with the desired name:
public class TheBaseClass
{
public List<int> BaseClassList { get; set; }
public virtual bool ShouldSerializeBaseClassList() { return true; }
}
public class TheDerivedClass : TheBaseClass
{
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public List<int> DerivedClassList { get { return BaseClassList; } set { BaseClassList = value; } }
public override bool ShouldSerializeBaseClassList() { return false; }
}
For an explanation of why this works see Defining Default Values with the ShouldSerialize and Reset Methods.
How to serialize the base class with derived classes
You can't magically serialize a derived class as it's base because
"...Serialization checks type of instance by calling Object.getType()
method. This method always returns the exact type of object."
http://bytes.com/topic/net/answers/809946-how-force-serialize-base-type
The solution here, if you really need to only serialize the base class is to implement the IXmlSerializable interface and create your own custom serializer.
IXmlSerializable:
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable(v=vs.110).aspx
One more thought. If you can work around the limitation of outputting the extra XML elements, you are able to serialize the derived class using only the base object by either 1) using XmlIncludeAttributes on the base class to tell it which types to expect or 2) using the XmlSerializer constructor overload that takes a list of types.
Edit:
After thinking about this a little more, a workaround would be that you would add a Clone() method onto your base object, then serialize the clone of the base.
Related Topics
What's the Point of the Var Keyword
Find Size of Object Instance in Bytes in C#
Invert "If" Statement to Reduce Nesting
Should I Use Public Properties and Private Fields or Public Fields for Data
How to Force Garbage Collector to Run
Linq to SQL - Left Outer Join with Multiple Join Conditions
How to Conditionally Apply a Linq Operator
How to Assign by "Reference" to a Class Field in C#
When to Dispose Cancellationtokensource
What Is [Serializable] and When Should I Use It
Export Datatable to Excel with Epplus
Shouldserialize*() VS *Specified Conditional Serialization Pattern
Multiple Httppost Method in Web API Controller