I have a class I found on another post that I'm trying to modify.
using System;
using System.IO;
namespace Misc
{
internal class ConfigManager
{
private string _sConfigFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format("{0}.xml", AppDomain.CurrentDomain.FriendlyName));
private Config m_oConfig = new Config();
public Config MyConfig
{
get { return m_oConfig; }
set { m_oConfig = value; }
}
// Load configuration file
public void LoadConfig()
{
if (System.IO.File.Exists(_sConfigFileName))
{
System.IO.StreamReader srReader = System.IO.File.OpenText(_sConfigFileName);
Type tType = m_oConfig.GetType();
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
object oData = xsSerializer.Deserialize(srReader);
m_oConfig = (Config)oData;
srReader.Close();
}
}
// Save configuration file
public void SaveConfig()
{
System.IO.StreamWriter swWriter = System.IO.File.CreateText(_sConfigFileName);
Type tType = m_oConfig.GetType();
if (tType.IsSerializable)
{
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
xsSerializer.Serialize(swWriter, m_oConfig);
swWriter.Close();
}
}
}
}
I'd like to pass in an object of type X and have it save. On that same premise, I'd like to pass in a type and have it pass back the object of type X. Right now, it is hard coded to use Config. So, if there is a way to pass in the class object (?) then I'd like it to save it as that object and/or return it of that object.
Is that possible? If so, how would I go about doing this?
Use generic:
internal class ConfigManager<T>
{
private string _fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format("{0}.xml", AppDomain.CurrentDomain.FriendlyName));
private T _config;
private XmlSerializer serializer = new XmlSerializer(typeof(T));
public T MyConfig
{
get { return _config; }
set { _config = value; }
}
public void LoadConfig()
{
if (File.Exists(_fileName))
{
using (var reader = File.OpenText(_fileName))
{
_config = (T)serializer.Deserialize(reader);
}
}
}
public void SaveConfig()
{
using (var writer = File.CreateText(_fileName))
{
serializer.Serialize(writer, _config);
}
}
}
Usage:
var man = new ConfigManager<Foo>();
Basically, we'd need to make it work with Generics. So, we start by giving it a type variable, and replace every use of the class Config with T:
internal class Manager<T>
{
private T m_oObj; // etc
Next, I'll just do one method; you can do the rest. (and I'm removed the explicit namespaces, cuz they're ugly)
using System.IO;
using System.Xml.Serialization;
public void LoadConfig<T>()
{
if (File.Exists(_sConfigFileName))
{
var srReader = File.OpenText(_sConfigFileName);
var xsSerializer = new XmlSerializer(typeof(T));
var oData = xsSerializer.Deserialize(srReader);
m_oObj = (T)oData;
srReader.Close();
}
}
Related
How can I create deserialization method that can take an object of class or any of derived classes?
public class Config
{
public string appname;
}
public class CustomConfig1 : Config
{
public string CustomConfig1Param1;
public string CustomConfig1Param2;
}
public class CustomConfig2 : Config
{
public string CustomConfig2Param1;
public string CustomConfig2Param2;
}
I want to get something like serialization method that defines type of input object:
public string serialize(object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
StringWriter serialized = new StringWriter();
serializer.Serialize(serialized, obj);
return serialized.ToString();
}
But when I read an XML from DB I can't define the type of object, so I can't pass it to XmlSerializer. It may be the Config object or any of derived classes
Please help. How can I define the type of input object?
[XmlInclude(typeof(CustomConfig1))]
[XmlInclude(typeof(CustomConfig2))]
public class Config
{
public string appname;
}
Then just serialize/deserialize specifying typeof(Config); the library will give you back an instance of the appropriate type based on the data.
Edit: full example, including the preference to not hard-code the sub-types:
using System;
using System.IO;
using System.Xml.Serialization;
public class Config
{
public string appname;
}
public class CustomConfig1 : Config
{
public string CustomConfig1Param1;
public string CustomConfig1Param2;
}
public class CustomConfig2 : Config
{
public string CustomConfig2Param1;
public string CustomConfig2Param2;
}
static class Program
{
static void Main()
{
var original = new CustomConfig1
{
appname = "foo",
CustomConfig1Param1 = "x",
CustomConfig1Param2 = "y"
};
var xml = Serialize(original);
var clone = DeserializeConfig(xml);
Console.WriteLine(clone.appname);
var typed = (CustomConfig1)clone;
Console.WriteLine(typed.CustomConfig1Param1);
Console.WriteLine(typed.CustomConfig1Param2);
}
public static string Serialize(Config obj)
{
using (var serialized = new StringWriter())
{
GetConfigSerializer().Serialize(serialized, obj);
return serialized.ToString();
}
}
public static Config DeserializeConfig(string xml)
{
using(var reader = new StringReader(xml))
{
return (Config)GetConfigSerializer().Deserialize(reader);
}
}
static Type[] GetKnownTypes()
{
// TODO: resolve types properly
return new[] { typeof(CustomConfig1), typeof(CustomConfig2) };
}
private static XmlSerializer configSerializer;
public static XmlSerializer GetConfigSerializer()
{
return configSerializer ?? (configSerializer =
new XmlSerializer(typeof(Config), GetKnownTypes()));
}
}
How to use a same StreamWriter across various methods in a class. For eg.
public class XMLWriter
{
public export(string filename)
{
StreamWriter sw = new StreamWriter(filename)
sw.write("Line1")
}
public footer()
{
// Note: I am not declaring streamwriter here since i want to use the same sw as in export method
sw.write("Line x N")
}
}
How can I use the same sw across many methods. Also this class will be instantiated from another class and the "public" methods will be called from there.
Any help will be highly appreciated.
Declare sw as a global variable, and only close and dispose it when you dispose your XMLWriter object (or when you know you won't write more into your file) with calling the DisposeWriter() method below from the class where you created that object:
public class MyClass
{
private void DoSomeStuff()
{
XMLWriter xmlwr = new XMLWriter();
xmlwr.export(#"C:\YourFile.txt");
xmlwr.footer();
xmlwr.DisposeWriter();
wmlwr = null;
}
}
public class XMLWriter
{
private StreamWriter sw;
public XMLWriter()
{
//this is the constructor, what you call with "new XMLWriter()"
}
public void export(string filename)
{
sw = new StreamWriter(filename)
sw.write("Line1")
}
public void footer()
{
sw.write("Line x N")
}
public void DisposeWriter()
{
sw.Close();
sw.Dispose();
}
}
I would just declare thee steamwriter above the methods (global variable) and do the work inside the methods
Pass it as parameter or use a private field - depends on you requirements.
using System;
using System.IO;
using System.Text;
public class XMLWriter
{
//Objs
private StreamWriter sw;
private StringBuilder sb;
//static items
private string strHeader;
private string strFooter;
public XMLWriter()
{
//this is the constructor, what you call with "new XMLWriter()"
}
public void export(string filename)
{
sb = new StringBuilder();
sw = new StreamWriter(filename);
sw.Write(strHeader + sb.ToString() + strFooter);
sw.Close();
sw.Dispose();
}
public string Footer
{
set
{
strFooter = value;
}
}
public string Header
{
set
{
strHeader = value;
}
}
public string LinesAdd
{
set
{
sb.Append(value);
}
}
}
How can I create deserialization method that can take an object of class or any of derived classes?
public class Config
{
public string appname;
}
public class CustomConfig1 : Config
{
public string CustomConfig1Param1;
public string CustomConfig1Param2;
}
public class CustomConfig2 : Config
{
public string CustomConfig2Param1;
public string CustomConfig2Param2;
}
I want to get something like serialization method that defines type of input object:
public string serialize(object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
StringWriter serialized = new StringWriter();
serializer.Serialize(serialized, obj);
return serialized.ToString();
}
But when I read an XML from DB I can't define the type of object, so I can't pass it to XmlSerializer. It may be the Config object or any of derived classes
Please help. How can I define the type of input object?
[XmlInclude(typeof(CustomConfig1))]
[XmlInclude(typeof(CustomConfig2))]
public class Config
{
public string appname;
}
Then just serialize/deserialize specifying typeof(Config); the library will give you back an instance of the appropriate type based on the data.
Edit: full example, including the preference to not hard-code the sub-types:
using System;
using System.IO;
using System.Xml.Serialization;
public class Config
{
public string appname;
}
public class CustomConfig1 : Config
{
public string CustomConfig1Param1;
public string CustomConfig1Param2;
}
public class CustomConfig2 : Config
{
public string CustomConfig2Param1;
public string CustomConfig2Param2;
}
static class Program
{
static void Main()
{
var original = new CustomConfig1
{
appname = "foo",
CustomConfig1Param1 = "x",
CustomConfig1Param2 = "y"
};
var xml = Serialize(original);
var clone = DeserializeConfig(xml);
Console.WriteLine(clone.appname);
var typed = (CustomConfig1)clone;
Console.WriteLine(typed.CustomConfig1Param1);
Console.WriteLine(typed.CustomConfig1Param2);
}
public static string Serialize(Config obj)
{
using (var serialized = new StringWriter())
{
GetConfigSerializer().Serialize(serialized, obj);
return serialized.ToString();
}
}
public static Config DeserializeConfig(string xml)
{
using(var reader = new StringReader(xml))
{
return (Config)GetConfigSerializer().Deserialize(reader);
}
}
static Type[] GetKnownTypes()
{
// TODO: resolve types properly
return new[] { typeof(CustomConfig1), typeof(CustomConfig2) };
}
private static XmlSerializer configSerializer;
public static XmlSerializer GetConfigSerializer()
{
return configSerializer ?? (configSerializer =
new XmlSerializer(typeof(Config), GetKnownTypes()));
}
}
I have a list of many test implements the interface IDoTest, that I want to store in a file. I also want to read from this file.
It seemed natural to simple use the XmlSerializer to store the objects in my List of IDoTest. But when I do this I get a vague I am sorry I cant do that error in the neighborhood of System.Xml.Serialization.TypeDesc.CheckSupported()
Can the XmlSerializer only do trivial jobs? Or am I missing something? They are talking about custom serialization at MSDN.
Here is my simplified code example.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public interface IDoTest
{
void DoTest();
void Setup();
}
internal class TestDBConnection : IDoTest
{
public string DBName;
public void DoTest()
{
Console.WriteLine("DoHardComplicated Test");
}
public void Setup()
{
Console.WriteLine("SetUpDBTest");
}
}
internal class PingTest : IDoTest
{
public string ServerName;
public void DoTest()
{
Console.WriteLine("MaybeDoAPing");
}
public void Setup()
{
Console.WriteLine("SetupAPingTest");
}
}
internal class Program
{
private static void Main(string[] args)
{
TestDBConnection Do1 = new TestDBConnection { DBName = "SQLDB" };
PingTest Do2 = new PingTest { ServerName = "AccTestServ_5" };
List<IDoTest> allTest = new List<IDoTest> { Do1, (Do2) };
// Now I want to serialize my list.
// Its here where I get the error at allTest
XmlSerializer x = new XmlSerializer(allTest.GetType());
StreamWriter writer = new StreamWriter("mySerializedTestSuite.xml");
x.Serialize(writer, allTest);
}
}
}
XmlSerializer cannot serialize an interface, and by extension, it cannot serialize a List<> of some interface. It can only serialize concrete object types.
The assumption is that you will probably want to deserialize the objects at some point, and if it only output information pertaining to that interface, it can't guarantee that all the necessary data is present to reconstruct the original objects.
This post shows a potential workaround, if you are able to use an abstract base class and explicitly provide every possible type of object that may appear in the list.
I followed the link that StriplingWarrior gave and found this excellent answer. https://stackoverflow.com/a/15089253/648076 from webturner
I changed his implementation and made a class class ListOfToDo that implemented both List and IXmlSerializable. That worked!
Here is my changed code.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public interface IDoTest
{
void DoTest();
void Setup();
}
public class TestDBConnection : IDoTest
{
public string DBName;
public void DoTest()
{
Console.WriteLine("DoHardComplicated Test");
}
public void Setup()
{
Console.WriteLine("SetUpDBTest");
}
}
public class PingTest : IDoTest
{
public string ServerName;
public void DoTest()
{
Console.WriteLine("MaybeDoAPing");
}
public void Setup()
{
Console.WriteLine("SetupAPingTest");
}
}
public class ListOfToDo : List<IDoTest>, **IXmlSerializable**
{
#region IXmlSerializable
public XmlSchema GetSchema(){ return null; }
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("ListOfToDo");
while (reader.IsStartElement("IDoTest"))
{
Type type = Type.GetType(reader.GetAttribute("AssemblyQualifiedName"));
XmlSerializer serial = new XmlSerializer(type);
reader.ReadStartElement("IDoTest");
this.Add((IDoTest)serial.Deserialize(reader));
reader.ReadEndElement(); //IDoTest
}
reader.ReadEndElement(); //IDoTest
}
public void WriteXml(XmlWriter writer)
{
foreach (IDoTest test in this)
{
writer.WriteStartElement("IDoTest");
writer.WriteAttributeString("AssemblyQualifiedName", test.GetType().AssemblyQualifiedName);
XmlSerializer xmlSerializer = new XmlSerializer(test.GetType());
xmlSerializer.Serialize(writer, test);
writer.WriteEndElement();
}
}
#endregion
}
internal class Program
{
private static void Main(string[] args)
{
TestDBConnection Do1 = new TestDBConnection { DBName = "SQLDB" };
PingTest Do2 = new PingTest { ServerName = "AccTestServ_5" };
ListOfToDo allTest = new ListOfToDo { Do1, (Do2) };
// Now I want to serialize my list.
// Its here where I get the error at allTest
XmlSerializer x = new XmlSerializer(allTest.GetType());
StreamWriter writer = new StreamWriter("mySerializedTestSuite.xml");
x.Serialize(writer, allTest);
writer.Flush();
writer.Close();
//Read it aka deserialize
{
var xmlSerializer = new XmlSerializer(typeof(ListOfToDo));
var xmlReader = XmlReader.Create(new StreamReader("mySerializedTestSuite.xml"));
ListOfToDo readWhatToTest = (ListOfToDo)xmlSerializer.Deserialize(xmlReader);
xmlReader.Close();
}
}
}
}
Output will then be:
<?xml version="1.0" encoding="utf-8"?>
<ListOfToDo>
<IDoTest AssemblyQualifiedName="ConsoleApplication1.TestDBConnection, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<TestDBConnection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DBName>SQLDB</DBName>
</TestDBConnection>
</IDoTest>
<IDoTest AssemblyQualifiedName="ConsoleApplication1.PingTest, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<PingTest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ServerName>AccTestServ_5</ServerName>
</PingTest>
</IDoTest>
</ListOfToDo>
Not sure if this might be the cause of your issue but on these two examples they do use typeof(T) instead of T.GetType()
http://msdn.microsoft.com/en-us/library/71s92ee1.aspx
I can't serialize a list of objects in C# with XmlSerializer
I have a List<object> with different types of objects in it like integers, strings, and custom types. All custom types are protobuf-adjusted.
What I wanna do now is to serialize / deserialize this list with protobuf.net. Up until now I suspect that I have to declare each and every type explicitly, which is unfortunately not possible with these mixed-list constructs. Because the binary formater has no problems to do these things I hope that I missed something and that you can help me out.
So my question is how to deal with objects in protobuf.net.
(disclosure: I'm the author of protobuf-net)
BinaryFormatter is a metadata-based serializer; i.e. it sends .NET type information about every object serialized. protobuf-net is a contract-based serializer (the binary equivalent of XmlSerializer / DataContractSerializer, which will also reject this).
There is no current mechanism for transporting arbitrary objects, since the other end will have no way of knowing what you are sending; however, if you have a known set of different object types you want to send, there may be options. There is also work in the pipeline to allow runtime-extensible schemas (rather than just attributes, which are fixed at build) - but this is far from complete.
This isn't ideal, but it works... it should be easier when I've completed the work to support runtime schemas:
using System;
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(10, typeof(DataItem<int>))]
[ProtoInclude(11, typeof(DataItem<string>))]
[ProtoInclude(12, typeof(DataItem<DateTime>))]
[ProtoInclude(13, typeof(DataItem<Foo>))]
abstract class DataItem {
public static DataItem<T> Create<T>(T value) {
return new DataItem<T>(value);
}
public object Value {
get { return ValueImpl; }
set { ValueImpl = value; }
}
protected abstract object ValueImpl {get;set;}
protected DataItem() { }
}
[ProtoContract]
sealed class DataItem<T> : DataItem {
public DataItem() { }
public DataItem(T value) { Value = value; }
[ProtoMember(1)]
public new T Value { get; set; }
protected override object ValueImpl {
get { return Value; }
set { Value = (T)value; }
}
}
[ProtoContract]
public class Foo {
[ProtoMember(1)]
public string Bar { get; set; }
public override string ToString() {
return "Foo with Bar=" + Bar;
}
}
static class Program {
static void Main() {
var items = new List<DataItem>();
items.Add(DataItem.Create(12345));
items.Add(DataItem.Create(DateTime.Today));
items.Add(DataItem.Create("abcde"));
items.Add(DataItem.Create(new Foo { Bar = "Marc" }));
items.Add(DataItem.Create(67890));
// serialize and deserialize
var clone = Serializer.DeepClone(items);
foreach (DataItem item in clone) {
Console.WriteLine(item.Value);
}
}
}
List<YourClass> list;
ProtoBuf.Serializer.Deserialize<List<YourClass>>(filestream);
There is a way of doing this, albeit not a very clean way, by using a wrapper object that utilises another serialisation mechanism that supports arbitrary objects. I am presenting an example below using JSON but, like I said, resorting to a different serialisation tool seems like defeating the purpose of using protobuf:
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
[DataMember(Order = 2)]
private readonly string _serialisedType;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
_serialisedContent = JsonConvert.SerializeObject(content);
_serialisedType = content.GetType().FullName;
Content = content;
}
[ProtoAfterDeserialization]
private void Initialise()
{
var type = Type.GetType(_serialisedType);
Content = type != null
? JsonConvert.DeserializeObject(_serialisedContent, type)
: JsonConvert.DeserializeObject(_serialisedContent);
}
}
EDIT: This can also be done using C#'s built-in binary serialisation
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, content);
stream.Flush();
stream.Position = 0;
_serialisedContent = Convert.ToBase64String(stream.ToArray());
}
}
[ProtoAfterDeserialization]
private void Initialise()
{
var data = Convert.FromBase64String(source);
using (var stream = new MemoryStream(data))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(stream);
}
}
}