XML Serialize List of generic objects derived from an interface,,, - c#

So I'm trying to XML serialize a List<IObject> derrived from an interface, but the IObjects are generic types... best resort to code:
public interface IOSCMethod
{
string Name { get; }
object Value { get; set; }
Type Type { get; }
}
public class OSCMethod<T> : IOSCMethod
{
public string Name { get; set; }
public T Value { get; set; }
public Type Type { get { return _type; } }
protected string _name;
protected Type _type;
public OSCMethod() { }
// Explicit implementation of IFormField.Value
object IOSCMethod.Value
{
get { return this.Value; }
set { this.Value = (T)value; }
}
}
And I have a List of IOSCMethods:
List<IOSCMethod>
of which I add objects to in the following way:
List<IOSCMethod> methodList = new List<IOSCMethod>();
methodList.Add(new OSCMethod<float>() { Name = "/1/button1", Value = 0 });
methodList.Add(new OSCMethod<float[]>() { Name = "/1/array1", Value = new float[10] });
And it's this methodList which is what I'm trying to serialize. But everytime I try, either I get a "Can't serialize an interface" but when I make it (either the IOSCMethod or the OSCMethod<T> class) implement IXmlSerializable I get the problem of "can't serialize an object with a parameterless constructor. but obviously I can't because it's an interface! lame pants.
Any thoughts?

Is this what you want:
[TestFixture]
public class SerializeOscTest
{
[Test]
public void SerializeEmpTest()
{
var oscMethods = new List<OscMethod>
{
new OscMethod<float> {Value = 0f},
new OscMethod<float[]> {Value = new float[] {10,0}}
};
string xmlString = oscMethods.GetXmlString();
}
}
public class OscMethod<T> : OscMethod
{
public T Value { get; set; }
}
[XmlInclude(typeof(OscMethod<float>)),XmlInclude(typeof(OscMethod<float[]>))]
public abstract class OscMethod
{
}
public static class Extenstion
{
public static string GetXmlString<T>(this T objectToSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(objectToSerialize.GetType());
StringBuilder stringBuilder = new StringBuilder();
string xml;
using (var xmlTextWriter = new XmlTextWriter(new StringWriter(stringBuilder)))
{
xmlSerializer.Serialize(xmlTextWriter, objectToSerialize);
xml = stringBuilder.ToString();
}
return xml;
}
}

Related

Problems with deserializing FieldInfos and PropertyInfos when using Newtonsoft.Json libraries

I am trying to serialize and deserialize FieldInfo and PropertyInfos, and I am getting this error, which I dont understand, nor do I know how to fix or change what I am doing to fix this: Newtonsoft.Json.JsonSerializationException: Could not create an instance of type System.Reflection.FieldInfo. Type is an interface or abstract class and cannot be instantiated.
The basis of what I am trying to do is serialize and deserialize a class called AutoLType that looks like this:
public class AutoLType
{
public static implicit operator AutoSType(AutoLType l)
{
if (l.PropertyOrField == PropOrField.Field)
{
return new AutoSType(l.Field, l.Attribute, AutoSType.PropOrField.Field);
}
else
{
return new AutoSType(l.Property, l.Attribute, AutoSType.PropOrField.Prop);
}
}
public enum PropOrField
{
Prop,
Field
}
public PropertyInfo Property { get; set; }
public FieldInfo Field { get; set; }
public AutoLoad Attribute { get; set; }
public PropOrField PropertyOrField { get; set; }
public AutoLType(object pOrF, AutoLoad attr, PropOrField propOrField)
{
if (pOrF is PropertyInfo)
{
Property = (PropertyInfo)pOrF;
}
else if(pOrF is FieldInfo)
{
Field = (FieldInfo)pOrF;
}
Attribute = attr;
PropertyOrField = propOrField;
}
}
public AutoLType Load(AutoLType autoLTypeToLoad)
{
AutoLType a = null;
try
{
string filePath = Application.persistentDataPath + autoLTypeToLoad.Attribute.FilePath;
string json = File.ReadAllText(filePath);
a = JsonConvert.DeserializeObject<AutoLType>(json);
}
catch (Exception ex)
{
Debug.Log(ex.ToString());
}
return a;
}
Let me know if you need to see the AutoLoad class, but it isnt giving any problems right now.

How to save the Interface collections in xml in wpf

I have a Model with interface collection. I want to save the collection in xml file at runtime in temp location. Without interface collection the Model is saved correctly in xml file. But the interface collection in not saved in xml file. Please anyone help me to achieve this. My Model class structure is mentioned below,
MainWindowModel
public class MainWindowModel
{
private string header;
public string Header
{
get { return header; }
set { header = value; }
}
private bool isEditing = false;
public bool IsEditing
{
get { return isEditing; }
set { isEditing = value; }
}
public ObservableCollection<Details> DetailsCollection { get; set; }
}
Details
public class Details
{
public string Key { get; set; }
public ObservableCollection<IValue> Values { get; set; }
}
IValue
public interface IValue
{
int Id { get; set; }
string Name { get; set; }
}
FileReaderWriter
public class FileReaderWriter<T>
{
public string FileLocation;
public T Fetch()
{
if (File.Exists(FileLocation))
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
TextReader reader = new StreamReader(FileLocation);
object obj = deserializer.Deserialize(reader);
T XmlData = (T)obj;
reader.Close();
return XmlData;
}
return default(T);
}
public virtual string GetFileLocation()
{
return FileLocation;
}
public void Save(T model)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
var directory = Path.GetDirectoryName(FileLocation);
if (!string.IsNullOrEmpty(directory))
{
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
using (var writer = XmlWriter.Create(FileLocation))
{
serializer.Serialize(writer, model);
}
}
}
}
ReaderWriterClass
public class DetailsViewReaderWriter : FileReaderWriter<ObservableCollection<MainWindowModel>>
{
public DetailsViewReaderWriter()
{
FileLocation = ConfigurationManager.AppSettings["RecentFileLocation"];
}
public ObservableCollection<MainWindowModel> FetchFile()
{
var recentFile = Fetch();
return recentFile;
}
public override string GetFileLocation()
{
return FileLocation;
}
public void SaveFile(ObservableCollection<MainWindowModel> fileModel)
{
Save(fileModel);
}
}
App.config
<appSettings>
<add key="RecentFileLocation" value="D:\MyProject\RecentDetails.xml"/>
</appSettings>
The XmlSerializer cannot serialize Interface types. What you can do is to implement the IXmlSerializable interface. Please find below a rough! example. It is not tested. You should read carefully on how to implement the IXmlSerializable interface correctly. But to give you an idea and help to get started:
Adjust your IValue interface:
public interface IValue: IXmlSerializable
{
int Id { get; set; }
string Name { get; set; }
}
Implement the interface:
public class Value : IValue
{
public int Id { get; set; }
public string Name { get; set; }
public XmlSchema GetSchema() => null;
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
Name = reader.GetAttribute("Name");
Id = int.Parse(reader.GetAttribute("Id"));
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Id", Id.ToString());
writer.WriteAttributeString("Name", Name);
}
}

What's correct way to use default constructor for XmlSerializer?

Could you help me to find an error please?
I'm trying to use XmlSerialize:
public static void ProcessLines(List<string> allLines, out pfm pfm)
{
...
pfm = newPfm;
pfm forseril = new pfm("");
XmlSerializer mySerializer = new XmlSerializer(typeof(pfm));
StreamWriter myWriter = new StreamWriter("myFileName.xml");
mySerializer.Serialize(myWriter, forseril);
myWriter.Close();
}
And here is that thing that I think should be a default constructor:
[Serializable]
[XmlRoot(ElementName = "Pfm", Namespace = null)]
public class pfm
{
public pfm(string data)
{
this.data = data;
}
public string data;
public Ctl ctl
{
get;
set;
}
[XmlAttribute(AttributeName = "Name")]
public string Name
{
get;
set;
}
}
I used an istruction from Microsoft site: instruction
What XmlSerializer requires is a parameterless constructor -- a constructor with no arguments. Thus your pfm needs a constructor as follows:
public class pfm
{
pfm() : this("") { }
public pfm(string data)
{
this.data = data;
}
}
It doesn't need to be public. Sample fiddle.

How do I get DataContractJsonSerializer to use concrete type in type hint when serializing generic class from interface

I have a set of classes as follows: a Command, which Executes and stores a Result;
a Response, which is created as in order to return the Result in a serialized form (plus extra metadata which I've left out). The Response.Result must be of type object, as it is used for a bunch of different commands, each of which can have a Result of any type at all.
The Command is generic, and I'd like it to accept an interface rather than concrete type, but when I do, the serialized response contains the following type hint: "__type":"ResultOfanyType:#serialization"
rather than the following, which is generated when the command accepts a concrete type:
"__type":"ResultOfMyObjectDhOQ6IBI:#serialization"
I need the type hint to contain the concrete type rather than ResultOfanyType. Why are interfaces being treated differently in this context? Notice that when the Type is a direct property of the serialized Command, then the concrete type is contained in the type hint
I've tried changing the the Result's Response property typed to Result, but that has no effect.
Here is the code. Simply uncomment/comment the lines in Main where the command is created and known types listed for the alternative version.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
namespace serialization
{
class Program
{
static void Main(string[] args)
{
Response response = new Response();
response.ResponseStatus = "ok";
ConcreteCommand command = new ConcreteCommand(); //switch with line below to test inteface
//InterfaceCommand command = new InterfaceCommand();
command.Execute();
response.Results = command.Results;
List<Type> knownTypes = new List<Type>
{
typeof(Result<MyObject>), //switch with Interface lines below to test inteface
typeof(MyObject)
//typeof(Result<IMyObject>),
//typeof(IMyObject)
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
Stream stream = new MemoryStream();
serializer.WriteObject(stream, response);
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
string output = reader.ReadToEnd();
Console.WriteLine(output);
}
}
public interface IMyObject
{
string name { get; set; }
}
[DataContract]
[KnownType(typeof(MyObject))]
public class MyObject : IMyObject
{
[DataMember]
public string name { get; set; }
}
[DataContract]
public class Result<T>
{
[DataMember]
public string Status { get; set; }
[DataMember]
public T Item { get; set; }
}
public abstract class BaseCommand<T>
{
protected Result<T> results = new Result<T>();
protected T resultObject;
public object Results
{
get { return this.results; }
}
public T ResultObject
{
get { return this.resultObject; }
}
public abstract void Execute();
}
public class InterfaceCommand : BaseCommand<IMyObject>
{
public override void Execute()
{
IMyObject myobject = new MyObject();
myobject.name = "my object";
Result<IMyObject> result = new Result<IMyObject>();
result.Item = myobject;
result.Status = "ok";
this.results= result;
this.resultObject = myobject;
}
}
public class ConcreteCommand : BaseCommand<MyObject>
{
public override void Execute()
{
MyObject myobject = new MyObject();
myobject.name = "my object";
Result<MyObject> result = new Result<MyObject>();
result.Item = myobject;
result.Status = "ok";
this.results = result;
this.resultObject = myobject;
}
}
[DataContract]
public class Response
{
[DataMember]
public string ResponseStatus { get; set; }
[DataMember]
public object Results { get; set; }
}
}
Let's start with this question and that might explain everything.
I need the type hint to contain the concrete type rather than ResultOfanyType. Why are interfaces being treated differently in this context?
An interface is basically just a contract for what a class implementing it should contain and multiple classes could implement its members. For example.
public interface IPerson
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
public class Person : IPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class Contact : IPerson
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public string PhoneNumber { get; set; }
}
So when you call an IPerson what are you expecting back? A Person or a Contact? Each has an id and the basic components of a name, but each also has unique properties that IPerson doesn't even know exist. This is why when you try to get an interface to resolve to a concrete class, you're not going to get anywhere without some sort of factory class to figure out what you want. So in this case, if I wanted to resolve an IPerson, I'd add the following line of code...
var objectType = iPersonObject.GetType();
In your case, you'd want to try calling GetType() on result.Item. This tells .NET to look at the actual type of the object implementing the interface and return it.
How about this...
class Program
{
static void Main(string[] args)
{
Response response = new Response();
response.ResponseStatus = "ok";
//ConcreteCommand command = new ConcreteCommand(); //switch with line below to test inteface
InterfaceCommand command = new InterfaceCommand();
command.Execute();
response.Results = command.Results;
List<Type> knownTypes = new List<Type>
{
typeof(MyObject),
typeof(Result<MyObject>) //switch with line below to test inteface
//typeof(Result<IMyObject>)
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
Stream stream = new MemoryStream();
serializer.WriteObject(stream, response);
stream.Position = 0;
StreamReader reader = new StreamReader(stream);
string output = reader.ReadToEnd();
Console.WriteLine(output);
}
}
public interface IMyObject
{
string name { get; set; }
}
[DataContract]
public class MyObject : IMyObject
{
[DataMember]
public string name { get; set; }
}
[DataContract]
public class Result<T>
{
[DataMember]
public string Status { get; set; }
[DataMember]
public T Item { get; set; }
}
public abstract class BaseCommand
{
protected Result<IMyObject> results = new Result<IMyObject>();
public Result<IMyObject> Results
{
get { return this.results; }
}
public abstract void Execute();
}
public class InterfaceCommand : BaseCommand
{
public override void Execute()
{
IMyObject myobject = new MyObject();
myobject.name = "my object";
Result<IMyObject> result = new Result<IMyObject>();
result.Item = myobject;
result.Status = "ok";
this.results= result;
}
}
public class ConcreteCommand : BaseCommand
{
public override void Execute()
{
MyObject myobject = new MyObject();
myobject.name = "my object";
Result<IMyObject> result = new Result<IMyObject>();
result.Item = myobject;
result.Status = "ok";
this.results = result;
}
}
[DataContract]
public class Response
{
[DataMember]
public string ResponseStatus { get; set; }
[DataMember]
public Result<IMyObject> Results { get; set; }
}
Outputs...
{"__type":"Response:#ConsoleApplication2","ResponseStatus":"ok","Results":{"__ty
pe":"ResultOfanyType:#ConsoleApplication2","Item":{"__type":"MyObject:#ConsoleAp
plication2","name":"my object"},"Status":"ok"}}
If you're trying to make some sort of generic contract, you're going to have to have some sort of common base class/interface. It won't work with object but you can go ala COM and make your own IUnknown interface from which to create as many subclasses as you like, as long as they are included within your known types.

Assigning a default value to a property

I have an xml like so:
<Settings>
<User default="Programmer"></User>
<Level default="2"></Level>
<Settings>
This is deserialized to an object of type UserSettings:
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[XmlElement("User")]
public string User { get; set; }
[XmlElement("Level")]
public string Level { get; set; }
}
The UserSettings object gives whatever the values are there for the tags at runtime.
I want the class to return the default attribute value when either the tag is empty or the tag is absent in the incoming xml.
So if there is an object objUserSettings of type UserSettings then
objUserSettings.User
should give "Programmer", or whatever is in default attribute value in the xml if the tag User is empty.
Regards.
Adding another answer because I had some fun with this question. Take it or leave it, but this is probably how I would attack this feature.
Here's an answer that is more complicated, but it gives you type safety using generics and most of the heavy lifting is done in one base class (no need to copy/paste the same code over and over).
Added a property to UserSettings to show an example of another type...
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
public UserSettings()
{
User = new DefaultableStringValue();
Level = new DefaultableIntegerValue();
IsFullscreen = new DefaultableBooleanValue();
}
[XmlElement("User")]
public DefaultableStringValue User { get; set; }
[XmlElement("Level")]
public DefaultableIntegerValue Level { get; set; }
[XmlElement("IsFullscreen")]
public DefaultableBooleanValue IsFullscreen { get; set; }
}
Simple implementations of typed DefaultableValues...
[Serializable]
public class DefaultableStringValue : DefaultableValue<string>
{
public DefaultableStringValue() : base(s => s) { }
}
[Serializable]
public class DefaultableIntegerValue : DefaultableValue<int>
{
public DefaultableIntegerValue() : base(int.Parse) { }
}
[Serializable]
public class DefaultableBooleanValue : DefaultableValue<bool>
{
public DefaultableBooleanValue() : base(bool.Parse) { }
}
Base class that does all of the heavy lifting of parsing and caching parsed values...
[Serializable]
public abstract class DefaultableValue<T>
{
protected Func<string, T> _parsingFunc;
private string _valueText;
private T _cachedValue;
private bool _isValueCached;
private string _defaultText;
private T _cachedDefault;
private bool _isDefaultCached;
protected DefaultableValue(Func<string, T> parsingFunc)
{
_parsingFunc = parsingFunc;
_isValueCached = false;
_isDefaultCached = false;
}
[XmlAttribute("default")]
public string DefaultText
{
get { return _defaultText; }
set
{
_defaultText = value;
_isDefaultCached = false;
}
}
[XmlText]
public string ValueText
{
get { return _valueText; }
set
{
_valueText = value;
_isValueCached = false;
}
}
[XmlIgnore]
public T Default
{
get
{
if (_isDefaultCached)
return _cachedDefault;
if (HasDefault)
return ParseAndCacheValue(DefaultText, out _cachedDefault, out _isDefaultCached);
return default(T);
}
set
{
DefaultText = value.ToString();
_cachedDefault = value;
_isDefaultCached = true;
}
}
[XmlIgnore]
public T Value
{
get
{
if (_isValueCached)
return _cachedValue;
if (HasValue)
return ParseAndCacheValue(ValueText, out _cachedValue, out _isValueCached);
return Default;
}
set
{
ValueText = value.ToString();
_cachedValue = value;
_isValueCached = true;
}
}
[XmlIgnore]
public bool HasDefault { get { return !string.IsNullOrEmpty(_defaultText); } }
[XmlIgnore]
public bool HasValue { get { return !string.IsNullOrEmpty(_valueText); } }
private T ParseAndCacheValue(string text, out T cache, out bool isCached)
{
cache = _parsingFunc(text);
isCached = true;
return cache;
}
}
And a sample program to demonstrate usage...
public class Program
{
private static void Main(string[] args)
{
UserSettings userSettings = new UserSettings();
userSettings.User.Default = "Programmer";
userSettings.Level.Default = 2;
userSettings.Level.Value = 99;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UserSettings));
string serializedUserSettings;
using (StringWriter stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, userSettings);
serializedUserSettings = stringWriter.GetStringBuilder().ToString();
}
UserSettings deserializedUserSettings;
using (StringReader stringReader = new StringReader(serializedUserSettings))
{
deserializedUserSettings = (UserSettings)xmlSerializer.Deserialize(stringReader);
}
Console.Out.WriteLine("User: HasDefault={0}, Default={1}, HasValue={2}, Value={3}",
deserializedUserSettings.User.HasDefault ? "Yes" : "No",
deserializedUserSettings.User.Default,
deserializedUserSettings.User.HasValue ? "Yes" : "No",
deserializedUserSettings.User.Value);
Console.Out.WriteLine("Level: HasDefault={0}, Default={1}, HasValue={2}, Value={3}",
deserializedUserSettings.Level.HasDefault ? "Yes" : "No",
deserializedUserSettings.Level.Default,
deserializedUserSettings.Level.HasValue ? "Yes" : "No",
deserializedUserSettings.Level.Value);
Console.Out.WriteLine("IsFullscreen: HasDefault={0}, Default={1}, HasValue={2}, Value={3}",
deserializedUserSettings.IsFullscreen.HasDefault ? "Yes" : "No",
deserializedUserSettings.IsFullscreen.Default,
deserializedUserSettings.IsFullscreen.HasValue ? "Yes" : "No",
deserializedUserSettings.IsFullscreen.Value);
Console.ReadLine();
}
}
Try this
using System.ComponentModel;
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[DefaultValue("Yogesh")]
[XmlElement("User")]
public string User { get; set; }
[DefaultValue("1st")]
[XmlElement("Level")]
public string Level { get; set; }
}
For more info see this.
You can use Default Value attribute for the property.
In you case it will be,
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[XmlElement("User")]
[DefaultValue("Programmer")]
public string User { get; set; }
[XmlElement("Level")]
[DefaultValue(2)]
public string Level { get; set; }
}
I don't believe there is a way to tell string to use that default xml attribute. You will have to deserialize each of those object into a structure that has the default value as a property which is an xml attribute.
Here's an example...
[Serializable]
[XmlRoot("Settings")]
public class UserSettings
{
[XmlElement("User")]
public DefaultableValue User { get; set; }
[XmlElement("Level")]
public DefaultableValue Level { get; set; }
}
[Serializable]
public class DefaultableValue
{
[XmlAttribute("default")]
public string Default { get; set; }
[XmlText]
public string Value { get; set; }
}
And sample program to demonstrate usage...
public class Program
{
private static void Main(string[] args)
{
UserSettings userSettings = new UserSettings();
userSettings.User = new DefaultableValue()
{
Default = "Programmer",
Value = "Tyler"
};
userSettings.Level = new DefaultableValue()
{
Default = "2",
Value = "99"
};
XmlSerializer xmlSerializer = new XmlSerializer(typeof(UserSettings));
string serializedUserSettings;
using (StringWriter stringWriter = new StringWriter())
{
xmlSerializer.Serialize(stringWriter, userSettings);
serializedUserSettings = stringWriter.GetStringBuilder().ToString();
}
UserSettings deserializedUserSettings;
using (StringReader stringReader = new StringReader(serializedUserSettings))
{
deserializedUserSettings = (UserSettings)xmlSerializer.Deserialize(stringReader);
}
Console.Out.WriteLine("User: Default={0}, Actual={1}",
deserializedUserSettings.User.Default,
deserializedUserSettings.User.Value);
Console.Out.WriteLine("Level: Default={0}, Actual={1}",
deserializedUserSettings.Level.Default,
deserializedUserSettings.Level.Value);
}
}
(Note that I have the default values in code, but they very well could have come from the xml file)

Categories

Resources