The following codes works as is, but I would like to use a reference to the MyProperty class to be passed in the constructor
instead of the strongly typed references in the inline code.
How do I do this, I expected to pass a ref to MyProperty but everything I have tried fails
I would like PropertyClass to be able to handle any MyProperty classes i.e. no references to MyProperty in PropertyClass
Still learning so sorry if I have missed the obvious !
Many Thanks for any help
Sarah
PropertyClass pc = new PropertyClass(!here!); // Would like to pass MyProperty class here
pc.Settings.Add(new MyProperty("Fred", "Monday"));
pc.SaveXml("MyTest.xml");
public class MyProperty
{
[XmlAttribute]
public string MyPropName { get; set; }
[XmlElement]
public string MyPropData { get; set; }
// default constructor needs to be parameterless for serialisation.
public MyProperty()
{
}
public MyProperty(string Name, string Data)
{
MyPropName = Name;
MyPropData = Data;
}
}
public class PropertyClass
{
public List<MyProperty> Settings { get; set; }
public PropertyClass() // How to pass the required class here ?
{ // public PropertyClass( ref MyProperty myprop)
Settings = new List<MyProperty>();
}
public void SaveXml(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create))
{
XmlSerializer XML = new XmlSerializer(typeof(List<MyProperty>), new XmlRootAttribute("Settings"));
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
XML.Serialize(stream, Settings, namespaces);
}
}
}
I would change the definition of PropertyClass to
public class PropertyClass<T>
{
public List<T> Settings { get; set; }
public PropertyClass()
{
Settings = new List<T>();
}
public void SaveXml(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create))
{
XmlSerializer XML = new XmlSerializer(typeof(List<T>), new XmlRootAttribute("Settings"));
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
XML.Serialize(stream, Settings, namespaces);
}
}
}
The type parameter T specifies the type of the items in the List<T>, so that you can instantiate PropertyClass as follows
var pc = new PropertyClass<MyProperty>();
Or when you get tired of MyProperty you can change it to new PropertyClass<foo>() without changing it elsewhere.
Another nice feature that I like about generics is that you can actually place constraints on the type parameter in the line where you declare it like:
public class PropertyClass<T> where T : MyClass, IMyInterface, new()
This means that T has to be derived from MyClass, it has to implement IMyInterface and has to have a parameterless constructor. (Obviously you do not need to add all such constraints, but they can all be useful in certain cases).
I want to rant a little more, but I am sure you can play with it and find some uses for it.
I think you need a generic class.
public class PropertyClass<T>
{
public List<T> Settings { get; set; }
public PropertyClass()
{
Settings = new List<T>();
}
...
}
PropertyClass<MyProperty> pc = new PropertyClass<MyProperty>();
I must add that your naming is very unclear. PropertyClass should be called something like XmlableList. And MyProperty already exists and is called NameValuePair<string,string>
Possibly you are looking for generics:
public class PropertyClass<TMyProperty>
{
public List<TMyProperty> Settings { get; set; }
public PropertyClass()
{
Settings = new List<TMyProperty>();
}
..
}
Work on your naming, it wasn't immediately obvious that PropertyClass was actually a collection of properties; perhaps MyPropertyCollection would be better?
What you're looking for is called constructor overloading. Basically you specify the constructor again, but this time with parameters:
public MyPropertyCollection()
{
Settings = new List<MyProperty>();
}
public MyPropertyCollection(IEnumerable<MyProperty> collection)
{
Settings = new List<MyProperty>(collection);
}
Or to allow var col = new MyPropertyCollection(new MyProperty(), new MyProperty(), new MyProperty()) you can do:
public MyPropertyCollection(params MyProperty[] collection)
{
Settings = new List<MyProperty>(collection);
}
Though you should be careful with that, it doesn't feel right and should you later want to introduce additional parameters, well it could end bad.
Also, as you're basically wrapping a list, what you could also consider is the System.Collection.ObjectModel.Collection<T> class as a base:
// The Collection<MyProperty> base class is responsible for maintaining the list
public class MyPropertyCollection : Collection<MyProperty>
{
public MyPropertyCollection()
{
// Default base() constructor is called automatically
}
public MyPropertyCollection(IList<MyProperty> properties)
: base(properties)
{
// Overloaded constructor calls base constructor with collection of properties
}
public void SaveXml(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Create))
{
// Serializer should now target this very class
var xml = new XmlSerializer(typeof (this), new XmlRootAttribute("Settings"));
var namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
xml.Serialize(stream, this, namespaces);
}
}
}
You need to add a constructor that takes a MyProperty as an argument:
public PropertyClass(MyProperty myprop)
{
Settings = new List<MyProperty> {myprop};
}
Note that MyProperty is a reference type so ref is unnecessary here (it already is a reference).
this is how you call the constructor
PropertyClass pc = new PropertyClass(new MyProperty("Fred", "Monday"));
this is the constructor
Public MyProperty MyProperty { get; set; }
public PropertyClass(MyProperty _myProperty)
{
MyProperty = _myProperty
}
Related
The overall goal here is like this: We have a lot of CSV files of various names and format stored in Azure blob storage. We need to convert them to lists.
I have an interface:
public interface IGpasData
{
List<T> ConvertToList<T>(StreamReader reader);
}
And then here's an example of a class that Implements it:
public class GpasTableOfContent : IGpasData
{
public string TocProp0 { get; set; }
public string TocProp1 { get; set; }
public string TocProp2 { get; set; }
public List<T> ConvertToList<T>(StreamReader reader)
{
List<T> dataList = new List<T>();
while (!reader.EndOfStream)
{
var lineItem = reader.ReadLine();
GpasTableOfContent dataItem = new GpasTableOfContent
{
TocProp0 = lineItem.Split(',')[0],
TocProp1 = lineItem.Split(',')[1],
Type = lineItem.Split(',')[2]
};
dataList.Add(dataItem);
}
return dataList;
}
}
To keep going with the example of the class above, there is a file called ToC.csv. In a class that is designed to convert THAT file into a list, I make this call:
List<GpasTableOfContent> gpasToCList = ConvertCloudFileToList<GpasTableOfContent>("ToC.csv", "MyModel");
Some other possible examples:
List<GpasFoo> gpasFooList = ConvertCloudFileToList<GpasFoo>("foo.csv", "MyModel");
List<GpasBar> gpasBarList = ConvertCloudFileToList<GpasBar>("bar.csv", "MyModel");
Here's ConvertCloudFileToList:
private List<T> ConvertCloudFileToList<T>(string fileName, string modelName)
{
// Get the .csv file from the InProgress Directory
string filePath = $"{modelName}/{fileName}";
CloudFile cloudFile = _inProgressDir.GetFileReference(filePath);
List<T> dataList = new List<T>();
// Does the file exist?
if (!cloudFile.Exists())
return dataList;
using (StreamReader reader = new StreamReader(cloudFile.OpenRead()))
{
IGpasData gpasData = (IGpasData)Activator.CreateInstance<T>();
dataList = gpasData.ConvertToList<T>(reader);
}
return dataList;
}
And that brings us back to ConvertToList. The problem is here:
dataList.Add(dataItem);
Can not convert 'GpasFoo' to 'T'
Not sure how to work around this.
Any object that is an IGpasData is expected to be able to produce a List of any given type when provided with a StreamReader. GpasTableOfContent does not fulfill this requirement, it can only produce a list of its own type.
However it doesn't seem reasonable to have one type of GpasData be responsible for converting everything so I'd suggest moving the Type argument from the ConvertToList method into the interface. This way subclasses will only be responsible for converting lists of a particular type.
public interface IGpasData<T>
{
List<T> ConvertToList(StreamReader reader);
}
public class GpasTableOfContent : IGpasData<GpasTableOfContent>
{
//...
public List<GpasTableOfContent> ConvertToList(StreamReader reader)
{
//...
}
}
On a side note, creating an empty table of contents and then using it to read from a stream and produce a list of the real table of contents seems very clunky to me. In my opinion, the behaviour of creating these content objects should be moved into its own class.
You can't do what you want here without providing some additional logic. The problem is that you have a string from reading the CSV file, and you want to convert it to a T, but there is no rule for converting a string into any arbitrary type.
One approach would be to change the method to also take a delegate Func that is used to convert each line into a T. Then if, for example, your data is guaranteed to consist of doubles, you could pass t => Double.Parse(t) for that argument. Of course, this approach requires that you change the signature of the interface method you are implementing.
If you are not able to change the signature of the interface method, then all I can suggest is trying to handle a pre-defined set of types and throwing an exception for other types.
As other have pointed out, this design is flawed:
public interface IGpasData
{
List<T> ConvertToList<T>(StreamReader reader);
}
This contract says that an IGpasData should only know how deserialize anything. It doesn't make sense.
An IGpasData should know how to deserialize itself, and for this we would need a self-referencing interface:
public interface IGpasData<T> where T : IGpasData<T>
{
List<T> ConvertToList(StreamReader reader);
}
public class GpasBar: IGpasData<GpasBar>
{
public string MyPropertyA { get; set; }
public int MyPropertyB { get; set; }
public List<GpasBar> ConvertToList(StreamReader reader)
{
var results = new List<GpasBar>();
while (!reader.EndOfStream)
{
var values = reader.ReadLine().Split(',');
results.Add(new GpasBar()
{
PropertyA = values[0],
PropertyB = int.Parse(values[1]),
});
}
return results;
}
}
Or, an IGpasData should know how to populate itself from an array of values:
public interface IGpasData
{
void Populate(string[] values);
}
public class GpasBar
{
public string MyPropertyA { get; set; }
public int MyPropertyB { get; set; }
public void Populate(string[] values)
{
MyPropertyA = values[0];
MyPropertyB = int.Parse(values[1]);
}
}
public static List<T> ConvertCloudFileToList<T>(string fileName, string modelName)
where T : IGpasData, new()
{
// ...
using (StreamReader reader = new StreamReader(cloudFile.OpenRead()))
{
var results = new List<T>();
while (!reader.EndOfStream)
{
var item = new T();
item.Populate(reader.ReadLine().Split(','));
results.Add(item);
}
return results;
}
}
Using this 2nd approach, you can avoid duplicating the part about StreamReader and read lines.
Some (external) genius decided to provide us with XML:
<message_X>
<header>
<foo>Foo</foo>
<bar>Bar</bar>
</header>
<body>
<blah>
<yadda1 />
<yadda2 />
<yadda3 />
<contentX>
<!-- message_X specific content -->
</contentX>
</blah>
</body>
</message_X>
However, there are also other messages (say, message_Y and message_Z). These all have the exact same basic structure besides what is in the content node and, the reason for this question, the differing root-nodes:
<message_Y>
<header>
<foo>Foo</foo>
<bar>Bar</bar>
</header>
<body>
<blah>
<yadda1 />
<yadda2 />
<yadda3 />
<contentY>
<!-- message_X specific content -->
</contentY>
</blah>
</body>
</message_Y>
Why the root node isn't just named <message>, as I would've done it baffles me. Who comes up with this?
I have created an abstract class Message accordingly:
public abstract class Message {
public Header Header { get; set; }
public Body Body { get; set; }
}
public class Header {
public string Foo { get; set; }
public string Bar { get; set; }
}
// Etc...
I was hoping I could then do this:
[XmlInclude(typeof(XMessage))]
[XmlInclude(typeof(YMessage))]
public abstract class Message {
// ...
}
[XmlRoot("message_X")]
public class XMessage : Message {
// ...
}
[XmlRoot("message_Y")]
public class YMessage : Message {
// ...
}
But that doesn't work: InvalidOperationException: <message_X xmlns=''> was not expected.. To deserialize I use:
var ser = new XmlSerializer(typeof(Message));
using (var sr = new StringReader(xmlString))
return (Message)ser.Deserialize(sr);
I have no control over the XML and I'm not looking forward to implement this message again and again for each X, Y and Z.
I'll sort the Content part out by probably making Message into Message<T> and inheriting with specifying T etc. but that'll be of later concern.
I have also tried specifying Message as Type and XMessage and YMessage as ExtraTypes of the XmlSerializer Constructor but that didn't help either. I have also tried to go the DataContractSerializer route with DataContract, KnownType etc. annotations but this didn't work either.
I would appreciate tips / pointers on how to solve this in a clean fashion.
With #steve16351's** ideas I wrote the following deserializer (I'm not interested in serializing, only deserializing):
public class XmlDeserializer<T>
where T : class
{
// "Globally" caches T => Dictionary<xmlelementnames, type>
private static readonly ConcurrentDictionary<Type, IDictionary<string, Type>> _typecache = new ConcurrentDictionary<Type, IDictionary<string, Type>>();
// We store instances of serializers per type T in this pool so we need not create a new one each time
private static readonly ConcurrentDictionary<Type, XmlSerializer> _serializers = new ConcurrentDictionary<Type, XmlSerializer>();
// And all serializers get the same instance of XmlReaderSettings which, again saves creating objects / garbage collecting.
private static readonly XmlReaderSettings _readersettings = new XmlReaderSettings() { IgnoreWhitespace = true };
// Lookup for current T, with this we keep a reference for the current T in the global cache so we need one less dictionary lookup
private readonly IDictionary<string, Type> _thistypedict;
public XmlDeserializer()
{
// Enumerate T's XmlInclude attributes
var includes = ((IEnumerable<XmlIncludeAttribute>)typeof(T).GetCustomAttributes(typeof(XmlIncludeAttribute), true));
// Get all the mappings
var mappings = includes.Select(a => new
{
a.Type,
((XmlRootAttribute)a.Type.GetCustomAttributes(typeof(XmlRootAttribute), true).FirstOrDefault())?.ElementName
}).Where(m => !string.IsNullOrEmpty(m.ElementName));
// Store all mappings in our current instance and at the same time store the mappings for T in our "global cache"
_thistypedict = _typecache.GetOrAdd(typeof(T), mappings.ToDictionary(v => v.ElementName, v => v.Type));
}
public T Deserialize(string input)
{
// Read our input
using (var stringReader = new StringReader(input))
using (var xmlReader = XmlReader.Create(stringReader, _readersettings))
{
xmlReader.MoveToContent();
// Make sure we know how to deserialize this element
if (!_thistypedict.TryGetValue(xmlReader.Name, out var type))
throw new InvalidOperationException($"Unable to deserialize type '{xmlReader.Name}'");
// Grab serializer from pool or create one if required
var serializer = _serializers.GetOrAdd(type, (t) => new XmlSerializer(t, new XmlRootAttribute(xmlReader.Name)));
// Finally, now deserialize...
return (T)serializer.Deserialize(xmlReader);
}
}
}
This deserializer uses the XmlInclude attributes to figure out which classes are to be mapped with what element-name using the XmlRoot attributes. Usage couldn't be simpler:
var ser = new XmlDeserializer<Message>();
ser.Deserialize("<message_X>...");
It does some internal 'caching' and 'pooling' of objects to keep it memory/GC-friendly and quite performant. So this solves my root-node-different-name-for-each-type problem. Now I need to figure out how I'm going to handle the different content-nodes...
** Who has since deleted his answer for some unknown reason...
The second part of the question, the 'generic content' was easily solved:
public class Blah {
public Yadda1 Yadda1 { get; set; }
public Yadda2 Yadda2 { get; set; }
public Yadda3 Yadda3 { get; set; }
[XmlElement("contentX", typeof(ContentX))]
[XmlElement("contentY", typeof(ContentY))]
[XmlChoiceIdentifier(nameof(PayloadType))]
public Payload Payload { get; set; }
public PayloadType PayloadType { get; set; }
}
public abstract class Payload { ... }
public class ContentX : Payload { ... }
public class ContentY : Payload { ... }
[XmlType]
public enum PayloadType
{
[XmlEnum("contentX")]
ContentX,
[XmlEnum("contentY")]
ContentY,
}
This only leaves me wondering if I could apply the same idea on the root node...
private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
I want to create something like this, but this is won't work. it's just an example to demonstrate my goal as it's pretty common creating proprties for string and int and even T but I've never seen a List property
Is it even possible do such a thing, creating a property for type List ?
EDIT
I have a normal class that has normal properties (string properties, int properties, etc) but I have this property that stores user options, So on the presentation layer I had to convert them into a string so I could be able to store them in the Object. Now is it possible to have a property of type List to store the multivalues in a better and clean way, instead of converting information into one string and then split it and again join it! Thanks Again =D
EDIT2
private List<KeyValuePair<string, string>> _settings;
public List<KeyValuePair<string, string>> MySettings
{
get { return _settings; }
set { _settings = value; }
}
I used the exact code you posted but the property still won't appear in the object's instance, so I tried adding code in the get and set (I wonder why you left them empty or does it means something?) and also added a private variable in the class but still it doesn't appear in the properties of the object's instance!
I hope you could provide the exact code to implement this property and a simple code that assigns or retrieves from/to an instance of this class object
It's the first time to even hear about this KeyValuePair and all the tutorials are pretty simple and not for my case, sorry!
The Last Edit: After a lot of researching and the help of Mark Avenius I found the perfect answer. hope everyone can benefit from this.
NOW! HOW TO CREATE A PROPERTY FOR A LIST :
The Options Class
Public Class Options
{
private string id;
private int option;
public int ID
{
get { return id; }
set { id= value; }
}
public string Option
{
get { return option; }
set { option = value; }
}
}
The Users Class
public class Users
{
private int userId;
private string pass;
private List<Options> userOptions = new List<Options>();
public int ID
{
get { return userId; }
set { user = userId; }
}
public string Pass
{
get { return pass; }
set { pass = value; }
}
public List<Options> OptionsList
{
get { return userOptions; }
set { userOptions = value; }
}
}
The Presentation Layer
Users newUser = new Users ();
Options userOption = new Options ();
userOption.ID = int.Parse(txtBxID.Text);
userOption.Option = txtBxOption.Text;
Item.Options.Add(userOption);
T must be defined within the scope in which you are working. Therefore, what you have posted will work if your class is generic on T:
public class MyClass<T>
{
private List<T> newList;
public List<T> NewList
{
get{return newList;}
set{newList = value;}
}
}
Otherwise, you have to use a defined type.
EDIT: Per #lKashef's request, following is how to have a List property:
private List<int> newList;
public List<int> NewList
{
get{return newList;}
set{newList = value;}
}
This can go within a non-generic class.
Edit 2:
In response to your second question (in your edit), I would not recommend using a list for this type of data handling (if I am understanding you correctly). I would put the user settings in their own class (or struct, if you wish) and have a property of this type on your original class:
public class UserSettings
{
string FirstName { get; set; }
string LastName { get; set; }
// etc.
}
public class MyClass
{
string MyClassProperty1 { get; set; }
// etc.
UserSettings MySettings { get; set; }
}
This way, you have named properties that you can reference instead of an arbitrary index in a list. For example, you can reference MySettings.FirstName as opposed to MySettingsList[0].
Let me know if you have any further questions.
EDIT 3:
For the question in the comments, your property would be like this:
public class MyClass
{
public List<KeyValuePair<string, string>> MySettings { get; set; }
}
EDIT 4: Based on the question's edit 2, following is how I would use this:
public class MyClass
{
// note that this type of property declaration is called an "Automatic Property" and
// it means the same thing as you had written (the private backing variable is used behind the scenes, but you don't see it)
public List<KeyValuePair<string, string> MySettings { get; set; }
}
public class MyConsumingClass
{
public void MyMethod
{
MyClass myClass = new MyClass();
myClass.MySettings = new List<KeyValuePair<string, string>>();
myClass.MySettings.Add(new KeyValuePair<string, string>("SomeKeyValue", "SomeValue"));
// etc.
}
}
You mentioned that "the property still won't appear in the object's instance," and I am not sure what you mean. Does this property not appear in IntelliSense? Are you sure that you have created an instance of MyClass (like myClass.MySettings above), or are you trying to access it like a static property (like MyClass.MySettings)?
Simple and effective alternative:
public class ClassName
{
public List<dynamic> MyProperty { get; set; }
}
or
public class ClassName
{
public List<object> MyProperty { get; set; }
}
For differences see this post: List<Object> vs List<dynamic>
public class MyClass<T>
{
private List<T> list;
public List<T> MyList { get { return list; } set { list = value; } }
}
Then you can do something like
MyClass<int> instance1 = new MyClass<int>();
List<int> integers = instance1.MyList;
MyClass<Person> instance2 = new MyClass<Person>();
IEnumerable<Person> persons = instance2.MyList;
You could do this but the T generic parameter needs to be declared at the containing class:
public class Foo<T>
{
public List<T> NewList { get; set; }
}
It's possible to have a property of type List<T> but your class needs to be passed the T too.
public class ClassName<T>
{
public List<T> MyProperty { get; set; }
}
Either specify the type of T, or if you want to make it generic, you'll need to make the parent class generic.
public class MyClass<T>
{
etc
I have a list where i want the type to be definined by the call for example in the class i want something like
public class ClassName
{
public ListType<T> ListName { get; set; }
// other class items
}
and then the usage to be what sets the class type like
var className = new ClassName()
{
ListType<int> = data
};
so basically thats what i want, i have it working using dynamic so the class is
public class ClassName
{
public ListType<dynamic> ListName { get; set; }
// other class items
}
and the call is
var className = new ClassName()
{
ListType<dynamic> = data
};
this works but i would like to know if there is a better way to do this so i dont have to use dynamic
oh almost forgot to mention the ListType is
public class ListType<T> : List<T>
{
}
and so doesnt fail by having different types passed to it
thanks
edit:
realised my usage of the code on stack overflow differed from my code
the ListType has a constructor that takes 3 arguments so the usage is more
var className = new ClassName()
{
ListName = new ListType<Type>(x, y, z)
}
How about
public class ClassName<T>
{
public ListType<T> ListName { get; set; }
// other class items
}
then use it like this:
var className = new ClassName<int>()
{
ListName = data;
};
Slight addition to Bertrand's answer gives you a way to not repeat the type argument in you specific use case, or even not mention it:
public static class ClassName
{
public static ClassName<T> Create<T>(ListType<T> list)
{
return new ClassName<T> { ListName = list };
}
public static ClassName<T> Create<T>(params T[] list)
{
return new ClassName<T> { ListName = new ListType<T>(list) };
}
}
Using the first method, you can write something like
ClassName.Create(new ListType<SomeType>(x, y, z));
using the second method, you can even write
ClassName.Create(x, y, z);
and let the compiler figure out that T is SomeType, but that doesn't work always.
Note that ClassName is different class than ClassName<T> and you might want to name it differently, e.g. ClassNameFactory.
I have two classes SccmAction and TicketAction which both implement interface IDelivery. These classes will be transmitted on a processing queue from which I just want to pull the message and act upon the Deliver method.
It seems however that I cannot deserialize to an interface because when I attempt to do so a System.NotSupportedException is thrown. From what I have gathered the XMLSerializer requires a concrete type for serialization. Does this leave me having to create an abstract class which implements IDelivery and inheriting SccmAction and ArsAction from it? Have I missed something that?
Edit Further Clarification
I may have a design flaw but my purpose is to pull messages off of a queue containing these objects. My intention was to have all objects coming off of this queue implement the interface ensuring that the application processing the objects just operates on the Deliver method. Each objects implementation of Deliver would vary.
Code to Deserialize
using (SqlConnection connection =
new SqlConnection(ConnectionString))
{
SqlCommand command = new SqlCommand(ReceiveString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
byte[] sb = (byte[])reader["message_body"];
SccmAction sa = DeserializeObject<SccmAction>(sb);
IDelivery iD = DeserializeObject<IDelivery>(sb);
}
reader.Close();
}
public static T DeserializeObject<T>(byte[] ba)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
MemoryStream memoryStream = new MemoryStream(ba);
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
return (T)xs.Deserialize(memoryStream);
}
Classes and Interface
[Serializable]
public class SccmAction : IDelivery
{
public string MachineName { get; set; }
public string CollectionName { get; set; }
public string Action { get; set; }
public DateTime EntryDateTime { get; set; }
public SccmAction () { }
public void Deliver()
{
throw new NotImplementedException();
}
}
[Serializable]
public class ArsAction : IDelivery
{
public string MachineName { get; set; }
public string CustomerName { get; set; }
public ArsAction() { }
public void Deliver()
{
throw new NotImplementedException();
}
}
public interface IDelivery
{
void Deliver();
}
Simply put, you can't serialize an interface without any dirtiness. But why do you want to serialize your interface anyway, it doesn't hold any state. It only has a declaration for a method.
You might want to create a Serialize method inside your classes. So they can serialize themselves individually, they know which type they are.
Besides, how do you expect the XmlSerializer to Deserialize to an exact type without providing it? It can't simply pick what to Deserialize to....
You could however change this line
IDelivery iD = DeserializeObject<IDelivery>(sb);
to this
IDelivery iD = DeserializeObject<SccmAction>(sb);
If you dont know what type you are deserializing to initially (ArsAction or SccmAction), then you can do something like this.
public static IDelivery DeserializeFromString(string xml)
{
//replace this stream with whatever you are useing
StringReader strReader = new StringReader(xml);
XmlReader reader = new XmlTextReader(fs); //important to use XmlReader
reader.MoveToContent(); //move to root
String className = reader.Name.Trim(); //read the class name
//use the namespace IDelivery is located in
className = "IDeliveryNamespace." + className;
//get the type
Type classType = Type.GetType(className);
XmlSerializer serializer = new XmlSerializer(Type.GetType(className));
// Declare an object variable of the type to be deserialized.
IDelivery i;
// Use the Deserialize method to restore the object's state.
i = (IDelivery)Convert.ChangeType(serializer.Deserialize(reader),classType);
return i;
}
Of course this assumes that you your Serializable class Name is not changed. Meaning ArsAction class serializes to and as far as I can tell from what you posted that is the case.
This code is a little dirty, but it should give you a starting point.