Serialize in XmlElement list of XmlAttribute's - c#

I want to serialize a list of XmlAttribute's in a XmlElement.
Some general info about the program:
It's a program where you can add product's with some attributes like the name, a description, and some properties (height, width, brand, condition), but the user can choose how many he want to add (not a fixed number).
Here is the class diagramm of the program:
Here is the code of the ManagerRoot class:
[Serializable]
public class ManagerRoot
{
[XmlElement("ProductRoot")]
public ProductRoot ProductRoot = new ProductRoot();
}
Here is the code of the ProductRoot Class:
[Serializable]
public class ProductRoot
{
[XmlElement("Product")]
public List<Product> ProductList { get; set; }
private void addProduct()
{
//?
}
}
Here is the code of the Product class:
[Serializable]
public class Product
{
[XmlIgnore]
private string _header;
[XmlAttribute("Header")]
public string Header
{
get { return this._header; }
set { this._header = value; }
}
[XmlIgnore]
private string _description;
[XmlAttribute("Description")]
public string Description
{
get { return this._description; }
set { this._description = value; }
}
[XmlIgnore]
private string _link;
[XmlAttribute("Link")]
public string Link
{
get { return this._link; }
set { this._link = value; }
}
[XmlIgnore]
private List<string> _properties;
[XmlAttribute("Properties")]
public List<string> Properties
{
get
{
}
set
{
}
}
}
Now I don't know what i have to do with the getter and setter of the list inside the class. And how can I add a new product with a name, description, link & a filled properties list?

For collections you need to annotate the property with XmlArray and XmlArrayItem to define which is the contained item. Please follow the same ProductList pattern for all collection based properties.
namespace ConsoleApplication1
{
[Serializable]
public class ProductRoot
{
[XmlArray]
[XmlArrayItem(ElementName = "Product", Type = typeof(Product))]
public List<Product> ProductList { get; set; }
}
[Serializable]
public class Product
{
[XmlIgnore]
private string _header;
[XmlAttribute("Header")]
public string Header {
get { return this._header; }
set { this._header = value; }
}
[XmlIgnore]
private string _description;
[XmlAttribute("Description")]
public string Description {
get { return this._description; }
set { this._description = value; }
}
[XmlIgnore]
private string _link;
[XmlAttribute("Link")]
public string Link {
get { return this._link; }
set { this._link = value; }
}
[XmlIgnore]
private List<string> _properties;
[XmlAttribute("Properties")]
public List<string> Properties { get; set; }
}
static class Program
{
static void Main() {
var productRoot = new ProductRoot();
var products = new List<Product>();
products.Add(new Product() { Description = "A", Properties = new List<string> { "PropA" } });
products.Add(new Product() { Description = "B", Properties = new List<string> { "PropB" } });
productRoot.ProductList = products;
Test(productRoot);
}
static void Test<T>(T obj) {
using (MemoryStream stream = new MemoryStream()) {
XmlSerializer s = new XmlSerializer(typeof(T));
using (var stringWriter = new StringWriter()) {
using (var writer = XmlWriter.Create(stringWriter)) {
s.Serialize(stringWriter, obj);
}
Console.WriteLine(stringWriter.ToString());
}
}
}
}

Related

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);
}
}

COM visible List from C# to vb6

I have ClassLibrary (in visual studio 2010 C#) with a class Car:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("ClassLibrary1.Car")]
public class Car
{
public Car()
{
Name = "";
Parts = new List<string>();
}
public string Name { get; set; }
public List<string> Parts { get; set; }
}
But when I use it in vb6 project: there is no property "Parts":
http://i.imgur.com/x4h3BMp.jpg?1
What can I do to make property List<> visible?
Of course, the file "AssemblyInfo.cs" contains:
[assembly: ComVisible(true)]
P.S. I really do not want to create for each List the class, like this:
public class Parts
{
private List<string> _parts;
public Parts()
{
_parts = new List<string>();
}
public void Add(string part)
{
_parts.Add(part);
}
public string GetAt(int index)
{
if (0 <= index && index < _parts.Count)
return _parts[index];
return "";
}
public void Clear()
{
_parts.Clear();
}
public int Count{ get{ return _parts.Count; } }
}
because there are too many.
COM does not support generic collections; you'll have to use an array or ArrayList instead:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("ClassLibrary1.Car")]
public class Car
{
public Car()
{
Name = "";
Parts = new string[];
}
public string Name { get; set; }
public string[] Parts { get; set; }
}
If you want to use a List behind-the-scenes you could use a List<string> as a backing variable, then change the property accessors to translate the list to/from an array:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("ClassLibrary1.Car")]
public class Car
{
private List<string> _Parts;
public Car()
{
Name = "";
_Parts = new List<string>();
}
public string Name { get; set; }
public string[] Parts
{
get
{
return _Parts.ToArray();
}
set
{
_Parts = new List<string>(value);
}
}
}
I did not want to do so, but had:
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("ClassLibrary1.Car")]
public class Car
{
public Car()
{
Name = "";
Parts = new Parts();
}
public string Name { get; set; }
public Parts Parts { get; set; }
}
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("ClassLibrary1.Parts")]
public class Parts : IList<string>
{
private List<string> _parts;
public Parts()
{
_parts = new List<string>();
}
#region IList<string> Members
public int IndexOf(string item)
{
return _parts.IndexOf(item);
}
public void Insert(int index, string item)
{
_parts.Insert(index, item);
}
public void RemoveAt(int index)
{
_parts.RemoveAt(index);
}
public string this[int index]
{
get
{
return _parts[index];
}
set
{
_parts[index] = value;
}
}
#endregion
#region ICollection<string> Members
public void Add(string item)
{
_parts.Add(item);
}
public void Clear()
{
_parts.Clear();
}
public bool Contains(string item)
{
return _parts.Contains(item);
}
public void CopyTo(string[] array, int arrayIndex)
{
_parts.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _parts.Count; }
}
public bool Remove(string item)
{
return _parts.Remove(item);
}
#endregion
#region ICollection<string> Members
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
#endregion
#region IEnumerable<string> Members
public IEnumerator<string> GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
In vb6:
Option Explicit
Dim car As New ClassLibrary1.car
Dim parts As New ClassLibrary1.parts
Private Sub Form_Load()
parts.Add "wheel"
parts.Add "door"
parts.Add "hood"
parts.Add "trunk"
car.Name = "GAS-24"
Set car.parts = parts
car.parts.RemoveAt (0)
End Sub
Drawback of this approach is a lot of code:(
Let me know, if anyone knows something like this:
//**[MakeVisibleListInVB6]**
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("ClassLibrary1.Car")]
public class Car
{
public Car()
{
Name = "";
Parts = new Parts();
}
public string Name { get; set; }
public List<string> Parts { get; set; }
}

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)

Object serializing/deserializing doesn't work

I have an extension method for System.Object to serialize and deserialize objects using Json.Net. this is my Extension methods:
public static void SaveToFile(this object data, string FileName)
{
using (StreamWriter writer = new StreamWriter(FileName))
{
string encode = WpfApplication.Helper.Encrypt(JsonConvert.SerializeObject(data));
writer.Write(encode);
writer.Close();
}
}
public static void LoadFromFile<t>(this object data, string FileName)
{
using (StreamReader reader = new StreamReader(FileName))
{
data = JsonConvert.DeserializeObject<t>(WpfApplication.Helper.Decrypt(reader.ReadToEnd()));
reader.Close();
}
}
and It's the class that I want to deserialize:
public class CardPack
{
#region Properties
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private List<FlashCard> cards;
public List<FlashCard> Cards
{
get { return cards; }
set { cards = value; }
}
private bool registered;
public bool Registered
{
get { return registered; }
set { registered = value; }
}
private int currentCardIndex;
public int CurrentCardIndex
{
get { return currentCardIndex; }
set { currentCardIndex = value; }
}
public string RegisterKey { get; set; }
public string ViewName { get; set; }
public List<FlashCard> TodayCards { get; set; }
#endregion
~CardPack()
{
foreach (FlashCard card in cards)
{
card.Check();
}
currentCardIndex = 0;
TodayCards = null;
this.SaveToFile(string.Format(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Split(#"file:\\")[1] + #"\Packs\{0}.json", name));
}
but whenever I deserialize the class cards is empty and I don't know how to resolve the problem. Can anybody help me?
Update
I find the error when I had this code:
public CardPack(string Name)
{
this.name = Name;
this.LoadFromFile<CardPack>(string.Format(System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase).Split(#"file:\\")[1] + #"\Packs\{0}.json", name));
foreach (var item in cards)
{
if (item.NextTime == null)
{
int a = 0;
}
}
TodayCards = cards.Where(c => c.NextTime.Date == DateTime.Today).ToList();
}
because the application closed when it tries to run foreach (var item in cards)!
I asked here and found out that cards is empty!
update2 I serialized the CardPack object with a little different structure. in previous structure Cards property was read-only.
I found that data = JsonConvert.DeserializeObject<t>(WpfApplication.Helper.Decrypt(reader.ReadToEnd())); in extension method doesn't change to 'data' class then Cards in CardPack is always null. I'll ask a question to find out why I cant set the class from it's extension method later.

XML Serialize List of generic objects derived from an interface,,,

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;
}
}

Categories

Resources