Serialize and DeSerialized derived classes from the base class - c#

I am trying to create a base class where I can inherit from it (to add properties to the derived classes) and the utilized the Load and Save methods from the base class. I find myself writing the Load and Save over and over and I'd like to apply some DRY to it...
namespace Common
{
using System;
using System.IO;
using System.Xml.Serialization;
public abstract class ApplicationSettings
{
protected ApplicationSettings()
{
}
public static ApplicationSettings Load(string fileName)
{
if (!File.Exists(fileName))
{
return null;
}
XmlSerializer serializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamReader reader = new StreamReader(fileName))
{
ApplicationSettings param = (ApplicationSettings)serializer.Deserialize(reader);
reader.Close();
return param;
}
}
public void Save(string fileName)
{
XmlSerializer serializer = new XmlSerializer(typeof(ApplicationSettings));
using (StreamWriter writer = new StreamWriter(fileName))
{
serializer.Serialize(writer, this);
writer.Close();
}
}
}
}
Given this abstract class, I then want to derive a class such as:
namespace Common
{
using System;
public class ApplicationParameters : ApplicationSettings
{
public ApplicationParameters()
{
}
public string AuthorizationCode
{
get;
set;
}
public string ReferenceNumber
{
get;
set;
}
}
}
For the Derived class, I should be able to do something like
ApplicationParameters parameters =
ApplicationParmeters.Load("settings.xml");
However, in the implementation above, an compiler error occurs when I attempt to cast the ApplicationSettings to the ApplicationParameters class when I call the Load method in the base class.
Is there a way to do this?

Try replacing typeof(ApplicationSettings) with GetType().
Using this mechanism you will also tell the serializer that ApplicationParameters is a child class of ApplicationSettings. You do this via XmlInclude
[XmlInclude(typeof(ApplicationParameters))]
class ApplicationSettings
The latter is a requirements of the serializer because otherwise it won't know what class to instantiate.

Why are you using XmlSerializer ?
Unless you must control the way the output XML looks, DataContractSerializer is recommended
See here, for example

Make the top level class generic so that the Save/Load methods can support multiple types:
public abstract class ApplicationSettings<T>
{
public static T Load(string xml){ // Implementation }
public static void Save (T obj) { // Implementation }
}
public class ApplicationParameters : ApplicationSettings<ApplicationParameters>
{
}
Or you could just make the static methods themselves generic:
public abstract class ApplicationSettings
{
public static T Load<T>(string xml){ // implementation }
public static void Save<T>(T obj){ // implementation }
}
You will now notice that the Save/Load methods from the abstract parent class are strongly typed to the child so that the following line will work as expected:
ApplicationParameters parameters = ApplicationParameters.Load("settings.xml");
or
ApplicationParameters parameters =
ApplicationSettings.Load<ApplicationParameters>("settings.xml");
Depending on which method you use.

How about a constructor in your ApplicationParameters class that takes an ApplicationSettings as an argument and copy the shared properties from one to another? And then just set the not shared properties to be null or the default...

Related

Serializing Interface array

I am trying to implement a way to save a set of objects to file, and then read it back to objects again.
I want to serialize the objects to XML (or JSON). The objects consists of one master object which holds an array of all the other objects. The array is of the type Interface, to allow several different types of child objects with some common functionality.
Obviously, there will be a problem during deserialization because the type of the interface object is not known.
Example:
[Serializable]
public class MasterClass
{
public ImyInterface[] subObjects;
}
public interface ImyInterface
{
}
How can I serialize/deserialize these objects?
My suggestions:
Add information about the object type in the serialized data.
Use a different solution than interface.
This is not the only way to serialize your data, but it is a ready to use solution from the framework:
DataContractSerializer supports this is you don't mind adding attributes for each of the available implementations of the interface:
[DataContract]
[KnownType(typeof(MyImpl))] // You'd have to do this for every implementation of ImyInterface
public class MasterClass
{
[DataMember]
public ImyInterface[] subObjects;
}
public interface ImyInterface
{
}
public class MyImpl : ImyInterface
{
...
}
Serializing/deserializing:
MasterClass mc = ...
using (var stream = new MemoryStream())
{
DataContractSerializer ser = new DataContractSerializer(typeof(MasterClass));
ser.WriteObject(stream, mc);
stream.Position = 0;
var deserialized = ser.ReadObject(stream);
}
For JSON you could use DataContractJsonSerializer instead.
One solution is to use an abstract class instead of an interface:
public class MasterClass
{
public MyAbstractClass[] subObjects;
}
[XmlInclude(typeof(MyImpl ))] //Include all classes that inherits from the abstract class
public abstract class MyAbstractClass
{
}
public class MyImpl : MyAbstractClass
{
...
}
It can be serialized/deserialized with the XmlSerializer:
MasterClass mc = ...
using (FileStream fs = File.Create("objects.xml"))
{
xs = new XmlSerializer(typeof(MasterClass));
xs.Serialize(fs, mc);
}
using (StreamReader file = new StreamReader("objects.xml"))
{
XmlSerializer reader = new XmlSerializer(typeof(MasterClass));
var deserialized = reader.Deserialize(file);
}

Can one change the instance of an object with the member function of its class?

A member function should change the instance of the calling object. But my recent attempts didn't change the instance.
I want to provide a base class, which implements a function to deserialize a string (or file) to an object of the child class.
I tried to implement an extension method, but it doesn't work as expected. The calling instance doesn't change.
class ChildClass : BaseClass
{
[XmlAttribute(AttributeName = "attribute")]
public string Atribute { set; get; }
}
class BaseClass {}
class static BaseClassExtension
{
public static void Deserialize(this BaseClass bar, string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
bar = (BaseClass)serializer.Deserialize(reader);
}
}
}
class Program
{
public void Main()
{
ChildClass foo = new ChildClass();
foo.Deserialize("file.xml")
}
}
Within foo.Deserialize() the object "bar" is filled, but after the function ends, foo is still the "empty" new ChildClass() and does not have the content of bar.
Is it even possible to do such thing?
EDIT:
Ok lets forget the extension method. Another attempt was:
class ChildClass : BaseClass
{
[XmlAttribute(AttributeName = "atribute")]
public string Atribute { set; get; }
}
class BaseClass
{
public void Deserialize(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
XmlSerializer serializer = new XmlSerializer(this.GetType());
return (BaseClass)serializer.Deserialize(reader);
}
}
}
class Program
{
public void Main()
{
ChildClass foo = new ChildClass();
// looks pretty bad and seems quite inconvenient to me
foo = (ChildClass)foo.Deserialize("file.xml")
}
}
What I "need" is something like:
class BaseClass
{
public void Deserialize(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
XmlSerializer serializer = new XmlSerializer(this.GetType());
this = (BaseClass)serializer.Deserialize(reader);
}
}
}
Or can I use the constructor?
Best regards,
Martin
It is not possible with how this is structured. In your case, 'bar' is a local variable within the scope of the static method that, toward the end, you reassign to a different instance. You're not dealing with the same calling object.
Depending on the solution you're attempting to solve for, you could break the base/child classes apart into two separate classes, and have a member of type 'base' within 'child.'
I don't think it's possible to change the instantiated object from within itself. You can, however, put your initialization code into a static method as part of your base class, that explicitly return a new instance of the object that you can assign to a new variable. That way you are only instantiating your class once through the method.
basically you would be doing:
foo = (ChildClass)BaseClass.Deserialize("file.xml");
which would be a bit cleaner than reassigning foo from a method called from itself. Your BaseClass would then have:
public static BaseClass Deserialize(String filePath) { ... }

associate a string with a class so that I can retrieve the string if class is given as generic type parameter

For different classes I need to attach a string to the class (i.e. Class1 has the string hello, Class2 has the string world etc.). Then I will have a generic type parameter T somewhere that will be (at runtime) one of these classes. I need to be able to retrieve the associated string from that generic type parameter.
How do I set this up and make it work?
Since all classes are written by me, I can use every possible approach (e.g. define common interface for them or common base class or whatever).
I tried creating a base class that has a public static field containing the string, and for each actual class "overwrite" (hide base and create new) the string. But it turned that I still could not retrieve the string when only having the type parameter T.
public class BaseClass
{
public static string Get => "";
}
public class Class1 : BaseClass
{
public static new string Get => "hello";
}
public class Class2 : BaseClass
{
public static new string Get => "world";
}
public class Testing<T> where T : BaseClass
{
public void Test()
{
string s = T.Get;
// compiler error: "'T' is a type parameter, which is not valid in the given context"
// strangely though, BaseClass.Get and Class1.Get and Class2.Get work fine!
}
}
Real-world use case:
I have a static class MySerializer<T> that is supposed to deserialize objects of type T. During deserialization I want to validate if my object of type T conforms to the schema associated with type T.
In order to validate I need to add a schema first. For each class T that can be deserialized there is a different schema which I store in my project as an embedded resource, so each schema has a path (like a file path). That means: for each class T I need to associate a string (the path) with the class so that I am able to get that path out of T.
Here is the relevant part of my serializer and the schema adding process:
public static class MySerializer<T>
{
private static readonly XmlSerializer _mySerializer = new XmlSerializer(typeof(T));
private static readonly XmlReaderSettings _settings = new Func<XmlReaderSettings>(() =>
{
System.Reflection.Assembly assy = typeof(MySerializer<T>).Assembly;
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null,
XmlReader.Create(assy.GetManifestResourceStream(T.GetAssociatedString())));
// T.GetAssociatedString(): How to make this work?
return new XmlReaderSettings
{
Schemas = schemas,
ValidationType = ValidationType.Schema,
ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings |
XmlSchemaValidationFlags.ProcessIdentityConstraints
};
})();
public static T Deserialize(Stream strm)
{
using (XmlReader reader = XmlReader.Create(strm, _settings))
{
return (T)_mySerializer.Deserialize(reader);
}
}
}
Since static methods and generic type parameters don't work together in C# (thanks to Matthew Watson for linking to Eric Lippet's Blog) and I don't want to create a new instance of T solely to be able to call a method, I'll go with attributes.
[AttributeUsage(AttributeTargets.Class)]
class SomeStringAttribute : Attribute
{
public string SomeString { get; set; }
public SomeStringAttribute(string s)
{
SomeString = s;
}
}
[SomeString("hello")]
public class Class1
{
}
[SomeString("world")]
public class Class2
{
}
public class Testing<T>
{
public void Test()
{
string s =
((SomeStringAttribute)typeof(T).GetCustomAttributes(typeof(SomeStringAttribute),
false)[0]).SomeString;
}
}
You could use reflection to do this, but note that all the properties need to be static (your sample code above introduces non-static properties to the derived class).
Here's a compilable example:
using System;
namespace Demo
{
public class BaseClass
{
public static string Get => "";
}
public class Class1 : BaseClass
{
public new static string Get => "hello";
}
public class Class2 : BaseClass
{
public new static string Get => "world";
}
public class Testing<T> where T : BaseClass
{
public string Test()
{
var property = typeof(T).GetProperty("Get");
if (property != null)
return (string) property.GetValue(null, null);
return null;
}
}
class Program
{
static void Main()
{
var test1 = new Testing<Class1>();
Console.WriteLine(test1.Test()); // Prints "hello"
var test2 = new Testing<Class2>();
Console.WriteLine(test2.Test()); // Prints "world"
}
}
}
In this code, the where T : BaseClass isn't actually needed for it to compile and work, but you might want to keep it to make it clear that it is only supposed to be used with classes that inherit from BaseClass.

Serialization of class attributes?

Is the default XmlSerializer capable of serializing class attributes as Xml attributes?
[MyClassTypeAttribute(ClassType.MyClass)]
public MyClass : BaseClass {
}
would turn to
<myclass MyClassType="MyClass">
Reason:
I have a WCF service that sends me different objects through the same operation contract which all derive from BaseClass. To know which type of object it is and to cast it directly (and serialize it as Xml to write in a document afterwards), I'd like to have some 'type' attribute (enum).
One possibility is, of course, declaring a property as XmlAttribute
[XmlAttribute(params)]
public MyClassType { get; set; }
Problem here is: The XmlSerializer (DataContractSerializer as well, AFAIK) forces me to have a setter on every property. I know I can declare the setter as protected and it still works (XmlSerializer, you naughty little thing), but don't really like that solution because 1) I think there is a reason that I'm able to leave out the setter in POCOs usually and 2) declaring some properties as XmlAttributes and others as XmlElements is confusing (it's like putting dogs and cats into a cat goulash.
(Additionally, is it possible to force a derived class to declare certain attributes?)
[abstract MyClassTypeAttribute]
if it is about the type of your class here is an example:
[XmlIncludeAttribute(typeof(ConcreteFooOne))]
[XmlIncludeAttribute(typeof(ConcreteFooTwo))]
[XmlIncludeAttribute(typeof(ConcreteFooThree))]
[XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
public abstract partial class AbstractFoo
{
// Some abstract props etc.
}
[XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
public class ConcreteFooOne : AbstractFoo
{
public int MyProp { get; set; }
}
[XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
public class ConcreteFooTwo : AbstractFoo
{
}
[XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
public class ConcreteFooThree : AbstractFoo
{
}
class Program
{
static void Main(string[] args)
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(AbstractFoo));
using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate))
{
serializer.Serialize(stream, new ConcreteFooOne() { MyProp = 10 });
stream.Flush();
}
using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate))
{
var c = serializer.Deserialize(stream);
}
}
}
The code will serialize and include the type attribute and when you deserialize you will get the right instance.

How to mock/ioc a class in a static class?

I have class which calls a static class, the static class basically is wrapper for another class. I know i can't mock/ioc static classes but can do this for the non-static class?
below is a sample of my code structure
namespace lib.CanModify
{
public class Something
{
public void method()
{
var obj = lib.CanNotModify.StaticClass.DoSomething();
}
}
}
namespace lib.CanNotModify
{
public static class StaticClass
{
public static Node DoSomething()
{
/*The class i want to mock!*/
Node node = new Node(10);
return node;
}
}
}
please advice a way to mock the node class via mstest
the short answer is no!
You cannot mock concrete implementations of classes. You can only create instances of classes, you can only mock interfaces or base classes. A mock pretends to be a concrete class by implementing the properties of the interface or base class using inheritance. basically creating a new concrete instance of the class on the fly.
If you change your structure to:
public class Node() : INode
Then you could mock this:
var moqNode = new Mock<INode>();
(this is moq syntax btw)
you would then need to change your variable to type INode
INode node = new Node(10);
and then you'd actually also need to inject your dependancy:
public static Node DoSomething(INode node)
{
return node;
}
which would make a farce of the entire thing......?!
You could create a StaticClassWrapper and an interface IStaticClass, then inject IStaticClass into your method.
Then you can easily mock IStaticClass
namespace lib.CanModify
{
using lib.CanNotModify;
public class Something
{
public void method()
{
method(new StaticClassWrapper());
}
public void method(IStaticClass staticClass)
{
var obj = staticClass.DoSomething();
}
}
public interface IStaticClass
{
Node DoSomething();
}
public class StaticClassWrapper : IStaticClass
{
public Node DoSomething()
{
return lib.CanNotModify.StaticClass.DoSomething();
}
}
}
This is similar to how the ASP.NET MVC project made classes such as System.Web.HttpRequest mockable

Categories

Resources