Serialize a class that contains a list? - c#

I may be doing this all wrong here, but I created a class that contains a list and I need to serialize that list. (Is there a better way or other suggestions to have multiple interfaces without a list?)
I've been doing okay with serializing custom classes, but this one for some reason isn't working out.
[XmlRoot("interfaces", Namespace = "")]
[XmlInclude(typeof(Interface))]
public class Interfaces
{
[XmlArray("interfaces")]
[XmlArrayItem("interface")]
List<Interface> _IflList = new List<Interface>();
public List<Interface> IflList
{
get { return _IflList; }
set { _IflList = value; }
}
public void Add(Interface objInterface)
{
_IflList.Add(objInterface);
}
}
[XmlType("interface")]
public class Interface
{
string _name;
public string name
{
get { return _name; }
set { _name = value; }
}
public Interface(string name)
{
this._name = name;
}
}
I get the error There was an error reflecting type 'JunOSConfig.Interfaces' when trying to serialize with:
public string SerializeObject(object objToSerialize, bool StripXmlVer, bool FormatOutput)
{
string strReturn = "";
XmlSerializerNamespaces xns = new XmlSerializerNamespaces();
xns.Add("", "");
Type objType = typeof(JunOSConfig.Interfaces);
XmlSerializer xs = new XmlSerializer(objToSerialize.GetType());
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = StripXmlVer;
StringWriter sw = new StringWriter();
XmlWriter xw = XmlWriter.Create(sw, xws);
xs.Serialize(xw, objToSerialize, xns);
strReturn = sw.ToString();
if (FormatOutput)
{
return Convert.ToString(XElement.Parse(strReturn));
}
else
{
return strReturn;
}
}

In addition to your constructor with one parameter
public Interface(string name)
{
this._name = name;
}
you will need another, parameterless constructor, like
public Interface()
{
}
If you do not declare a constructor yourself, a constructor without
parameters will be generated.
Then it should work.

Related

Serilize a Nullable double property of class as XmlText

I have to serialize using the folowing code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace MyExample
{
class Program
{
static void Main(string[] args)
{
MyXmlDocument document = new MyXmlDocument();
document.MyExample.NodeA.value = "Value To Node A";
document.MyExample.NodeB.value = "Value To Node B";
document.MyExample.NodeC.value = 1234.567;
document.WriteToXml(#"C:\Users\E9JR\Desktop\mydocument.xml");
Console.Write("> Done!");
Console.ReadKey();
}
}
[XmlRoot(ElementName="xmlExample",IsNullable=false)]
public class XmlExample
{
private NodeA_Elem _nodea;
[XmlElement()]
public NodeA_Elem NodeA
{
get
{
return _nodea;
}
set
{
_nodea = value;
}
}
public bool ShouldSerializeNodeA()
{
return !String.IsNullOrEmpty(_nodea.value);
}
private NodeB_Elem _nodeb;
[XmlElement(ElementName = "NodeB", IsNullable = false)]
public NodeB_Elem NodeB
{
get
{
return _nodeb;
}
set
{
_nodeb = value;
}
}
public bool ShouldSerializeNodeB()
{
return !String.IsNullOrEmpty(_nodeb.value);
}
private NodeC_Elem _nodec;
[XmlElement(ElementName = "NodeC",IsNullable=false)]
public NodeC_Elem NodeC
{
get
{
return _nodec;
}
set
{
_nodec = value;
}
}
public bool ShouldSerializeNodeC()
{
return _nodec.value.HasValue;
}
public XmlExample()
{
_nodea = new NodeA_Elem();
_nodeb = new NodeB_Elem();
_nodec = new NodeC_Elem();
}
}
public class NodeA_Elem
{
[XmlText()]
public string value { get; set; }
}
public class NodeB_Elem
{
[XmlText()]
public string value { get; set; }
}
public class NodeC_Elem
{
[XmlText()]
public double? value { get; set; }
}
public class MyXmlDocument
{
private XmlExample _myexample;
public XmlExample MyExample
{
get
{
return _myexample;
}
set
{
_myexample = value;
}
}
public void WriteToXml(string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(XmlExample));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.Encoding = Encoding.Unicode;
StringWriter txtwriter = new StringWriter();
XmlWriter xmlwtr = XmlWriter.Create(txtwriter, settings);
serializer.Serialize(xmlwtr, MyExample);
StreamWriter writer = new StreamWriter(path);
writer.Write(txtwriter.ToString());
writer.Close();
}
public void ReadXml(string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(XmlExample));
StreamReader reader = new StreamReader(path);
MyExample = (XmlExample)serializer.Deserialize(reader);
}
public MyXmlDocument()
{
_myexample = new XmlExample();
}
}
}
I'm trying to serialize NodeC using as text for the node the value property, which is a double, but it's not working, even using the ShouldSerialize pattern to avoid serialize empty nodes. NodeA and NodeB is working fine. I need help for NodeC.
You can't serialize a nullable double as XmlText. If you look at the full text of the System.InvalidOperationException you are getting, you will see something like:
InnerException: System.InvalidOperationException
Message="Cannot serialize member 'value' of type System.Nullable`1[System.Double]. XmlAttribute/XmlText cannot be used to encode complex types."
Source="System.Xml"
StackTrace:
at System.Xml.Serialization.XmlReflectionImporter.ImportAccessorMapping(MemberMapping accessor, FieldModel model, XmlAttributes a, String ns, Type choiceIdentifierType, Boolean rpc, Boolean openModel, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.ImportFieldMapping(StructModel parent, FieldModel model, XmlAttributes a, String ns, RecursionLimiter limiter)
at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
That message is self explanatory. Confirmation from the documentation for XmlTextAttribute:
You can apply the XmlTextAttribute to public fields and public read/write properties that return primitive and enumeration types.
You can apply the XmlTextAttribute to a field or property that returns an array of strings. You can also apply the attribute to an array of type Object but you must set the Type property to string. In that case, any strings inserted into the array are serialized as XML text.
The XmlTextAttribute can also be applied to a field that returns an XmlNode or an array of XmlNode objects.
To understand why ShouldSerializeXXX() doesn't help here, you should understand that XmlSerializer works as follows:
The first time you serialize a type, the XmlSerializer constructor internally writes run-time c# code to serialize and deserialize instances of the type and all referenced types using reflection, then compiles the code and loads the resulting DLL into memory.
Subsequently, serialization and deserialization of class instances are performed by the previously created dynamic DLL.
But step 1 does not have access to the instance of the class. It creates its dynamic library based purely on type information. And, from the type information, there is no way to infer that the relevant ShouldSerializeXXX() method will return false when the double? value is null. Thus, dynamic code generation aborts, because code to write a nullable double as XmlText can't be generated.
As a workaround, you could make a string property that represents the double:
public class NodeC_Elem
{
[XmlIgnore]
public double? value { get; set; }
[XmlText]
public string StringValue
{
get
{
if (value == null)
return null;
return XmlConvert.ToString(value.Value);
}
set
{
if (value == null)
{
this.value = null;
return;
}
this.value = XmlConvert.ToDouble(value);
}
}
}

Serialization: How to Deserialize in C#

How can I deserialize the string based on what I have done in this method? Basically, what I have here is to pass the string through the network using serialization and deserialize the string in order to convey the message. But once I managed to receive the message, I have no idea if what I'm doing is correct. Here's the code:
string ConvertToString(FrogGame np, Frog1 pm, Frog2 pm2) //Serialization. the three parameters are the classes.
{
XmlSerializer sendSerializer = new XmlSerializer(typeof(FrogGame),new Type[]{typeof(Frog1),typeof(Frog2)});
StreamWriter myWriter = new StreamWriter(#"pad1.xml");
sendSerializer.Serialize(myWriter, np);
sendSerializer.Serialize(myWriter, pm);
sendSerializer.Serialize(myWriter, pm2);
return myWriter.ToString();
} //Overall, I serialize it into string
Once I pass the string through the network, I want to deserialize it in order the pass the message to the classes. How do I continue here onwards? How can I edit? The code:
void StringReceived(string str) //so str is myWriter.ToString()
{
XmlSerializer revSerializer = new XmlSerializer(typeof(FrogGame), new Type[] { typeof(Frog1), typeof(Frog2) });
FileStream myFileStream = new FileStream(#"pad1.xml", FileMode.Open);
FrogGame b = (FrogGame)revSerializer.Deserialize(myFileStream);
if (b is Frog1)
{
if (Network.IsServer())
{
pm = (Frog1)b;
pm.Position.Y = b.pm.Position.Y;
pm.Position.X = b.pm.Position.X;
}
else
{
System.Diagnostics.Debug.WriteLine("BAD Message: " + msg);
}
}
else if (b is Frog2)
{
if (Network.IsClient())
{
pm2 = (PaddleMessage2)b;
pm2.Position.Y = b.pm2.Position.Y;
pm2.Position.X = b.pm2.Position.X;
}
else
{
System.Diagnostics.Debug.WriteLine("BAD Message: " + msg);
}
}
}
I might misinterpret your problem, but I why don't you put all the thing you want to save in a class and do it like this (plus, if you use class, your data "transportation" and "management" will be much easier) :
SERIALIZATION
XmlSerializer serializer = new XmlSerializer(typeof(FrogGameData));
TextWriter textWriter = new StreamWriter("FrogGameSaveFile.xml");
serializer.Serialize(textWriter, _frogGameData);
textWriter.Close();
DESERIALIZATION
XmlSerializer deserializer = new XmlSerializer(typeof(FrogGameData));
TextReader textReader = new StreamReader("FrogGameSaveFile.xml");
_frogGameData = (FrogGameData)deserializer.Deserialize(textReader);
textReader.Close();
Note : The need-to-be-saved field should have property, because the tag in the XML will mimic the property name.
Additional Note : FrogGameData is not different than a normal class for automatic serialization like this. The XML will mimic your property order in the class for the one in the XML file.
But if you wanna need to rearrange the XML tag placement, you could do something like [XmlElement(Order = 1)],[XmlElement(Order = 2)], etc on top of your property to customize the order in XML file.
UPDATE
In case you need it, this is an example of your FrogGameData class :
public class FrogGameData
{
private Frog _frog1;
private Frog _frog2;
public Frog Frog1
{
get { return _frog1; }
set { _frog1 = value; }
}
public Frog Frog2
{
get { return _frog2; }
set { _frog2 = value; }
}
}
And the XML will pretty much like this :
<?xml version="1.0" encoding="utf-8"?>
<FrogGameData>
<Frog1>Something-depends-on-your-data</Frog1>
<Frog2>Something-depends-on-your-data</Frog2>
</FrogGameData>
But, if your class is (Note the XmlElement part) :
public class FrogGameData
{
private Frog _frog1;
private Frog _frog2;
[XmlElement(Order = 2)]
public Frog Frog1
{
get { return _frog1; }
set { _frog1 = value; }
}
[XmlElement(Order = 1)]
public Frog Frog2
{
get { return _frog2; }
set { _frog2 = value; }
}
}
Then, your XML will be :
<?xml version="1.0" encoding="utf-8"?>
<FrogGameData>
<Frog2>Something-depends-on-your-data</Frog2>
<Frog1>Something-depends-on-your-data</Frog1>
</FrogGameData>

how do I set the current class to the return types results

I have this class:
using System.IO;
using System.Xml.Serialization;
namespace ssscc.Settings
{
public class AppSettings
{
private string _companyName;
public string CompanyName
{
set { _companyName = value; }
get
{
if (string.IsNullOrWhiteSpace(_companyName))
{
LoadSettings();
}
return _companyName;
}
}
private string _companyPhone;
public string CompanyPhone
{
set
{
_companyPhone = value;
}
get
{
if (string.IsNullOrWhiteSpace(_companyPhone))
{
LoadSettings();
}
return _companyPhone;
}
}
private string GetSettingsFile()
{
var exePath = System.Windows.Forms.Application.StartupPath;
var sharedDirectory = Path.Combine(exePath, "shared");
var settingsDirectory = Path.Combine(sharedDirectory, "settings");
var settingsFile = Path.Combine(settingsDirectory, "ssscc.xml");
if (!Directory.Exists(sharedDirectory))
{
Directory.CreateDirectory(sharedDirectory);
}
if (!Directory.Exists(settingsDirectory))
{
Directory.CreateDirectory(settingsDirectory);
}
return settingsFile;
}
internal void SaveSettings(AppSettings settings)
{
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenWrite(GetSettingsFile()))
{
serializer.Serialize((Stream) stream, (object) settings);
}
}
internal void LoadSettings()
{
if (!File.Exists(GetSettingsFile()))
{
return;
}
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenRead(GetSettingsFile()))
{
var appsetting = (AppSettings) serializer.Deserialize(stream);
CompanyPhone = appsetting.CompanyPhone;
CompanyName = appsetting.CompanyName;
}
}
}
}
My question is about this code:
var appsetting = (AppSettings) serializer.Deserialize(stream);
CompanyPhone = appsetting.CompanyPhone;
CompanyName = appsetting.CompanyName;
I am pretty sure there is a way to return the appsettings directly to the class that contains the method so I do not have to loop through each property such as this:
CompanyPhone = appsetting.CompanyPhone;
CompanyName = appsetting.CompanyName;
Can I assign the properties directly without having to maintain this code?
You are getting a new instance of AppSettings while deserializing from file. You may use it, can't you? Try to replace LoadSettings with a static factory method like this:
internal static AppSettings GetInstance()
{
if (!File.Exists(GetSettingsFile()))
return null;
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenRead(GetSettingsFile()))
return (AppSettings)serializer.Deserialize(stream);
}
while to save your settings, you have no need to pass the settings object as an argument. I guess the following code should do the job:
internal void SaveSettings()
{
var serializer = new XmlSerializer(typeof(AppSettings));
using (var stream = File.OpenWrite(GetSettingsFile()))
serializer.Serialize((Stream)stream, this);
}
Use the factory GetInstance method to initialize settings (well, as an example):
var s = AppSettings.GetInstance();
if (s == null)
{
s = new AppSettings
{
CompanyName = "MyCompany",
CompanyPhone = "######"
};
s.SaveSettings();
}
P.S.: if properties getters and setters have no additional logic (LoadSettings method no longer exists), you could use auto-properties:
public string CompanyName { get; set; }
public string CompanyPhone { get; set; }
and GetSettingsFile may be declared as static, as it does not operate any of the instance class members:
private static string GetSettingsFile()
{
//...
return settingsFile;
}
Do you really need to have lazy-loading in here, if not, make your methods explicitly:
public class AppSettings
{
private static readonly XmlSerializer Serializer
= new XmlSerializer(typeof(AppSettings));
public string CompanyName { get; set; }
public string CompanyPhone { set; get; }
private static string GetSettingsFile()
{
return null;
}
public static void SaveSettings(AppSettings settings)
{
using (var stream = File.OpenWrite(GetSettingsFile()))
Serializer.Serialize(stream, settings);
}
internal static AppSettings LoadSettings()
{
if (!File.Exists(GetSettingsFile()))
return null;
object appsetting = null;
using (var stream = File.OpenRead(GetSettingsFile()))
appsetting = Serializer.Deserialize(stream);
return appsetting as AppSettings;
}
}
Do you can use:
var setting = AppSettings.LoadSettings();
and:
AppSettings.SaveSettings(setting);
Please note in here, creating XmlSerializer everytime will get the memory leak,
The XmlSerializer constructor will generate a pair of classes derived from XmlSerializationReader and XmlSerializationWriter by analyzing the Person class using reflection. It will create temporary C# files, compile the resulting files into a temporary assembly, and finally load that assembly into the process. Code gen like this is also relatively expensive. So the XmlSerializer caches the temporary assemblies on a per-type basis. This means that the next time an XmlSerializer for the Person class is created, the cached assembly is used rather than a new one generated.
Therefore, you should keep XmlSerializer as static.

Custom XML serialization with inheritance

I have the following class structure which I want to serialize:
[XmlRoot("SomeClass")]
public class SomeClass
{
private BaseClass[] _itemArray;
public BaseClass[] ItemArray
{
get { return _itemArray; }
set { _itemArray = value; }
}
public PPSPStatReportMessage()
: base()
{
}
}
public class SomeOtherClass
{
private int _property5;
[XmlAttribute("Property5")]
public int Property5
{
get { return _property5; }
set { _property5 = value; }
}
private string _property6;
[XmlText()]
public string Property6
{
get { return _property6; }
set { _property6 = value; }
}
public SomeOtherClass()
{
}
}
[XmlInclude(typeof(DerivedClass1))]
[XmlInclude(typeof(DerivedClass2))]
[XmlRoot("BaseClass")]
[XmlType("")]
public class BaseClass
{
private string _property1;
[XmlAttribute("Property1")]
public string Property1
{
get { return _property1; }
set { _property1 = value; }
}
public SomeClass(string PropertyVal)
{
_property1 = PropertyVal;
}
}
[XmlRoot("BaseClass")]
[XmlTypeAttribute(Namespace = "")]
public class DerivedClass1 : BaseClass
{
private string _property2;
[XmlAttribute("Property2")]
public string Property2
{
get { return _property2; }
set { _property2 = value; }
}
private SomeOtherClass _property3;
[XmlElement("SomeOtherClass")]
public SomeOtherClass Property3
{
get { return _property3; }
set { _property3 = value; }
}
public DerivedClass()
: base("PropertyVal1")
{
}
}
[XmlRoot("BaseClass", Namespace = "")]
[XmlType("")]
public class DerivedClass2 : BaseClass
{
private Int64 _property4;
[XmlAttribute("Property4")]
public Int64 Property4
{
get { return _property4; }
set { _property4 = value; }
}
public DerivedClass2()
: base("PropertyVal2")
{
}
}
And this is the method I use to serialize SomeClass:
public static string SerializeXML(object Value, System.Type ObjectType)
{
XmlSerializer serializer = new XmlSerializer(ObjectType);
XmlSerializerNamespaces namespaceSerializer = new XmlSerializerNamespaces();
namespaceSerializer.Add("", "");
StringWriter ms = new StringWriter();
serializer.Serialize(ms, Value, namespaceSerializer);
return ms.ToString();
}
This method generates an XML structure that looks like the following:
<?xml version="1.0" encoding="utf-16"?>
<SomeClass>
<ItemArray>
<BaseClass d3p1:type="DerivedClass1" Property1="PropertyVal1" Property2="123" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">
<SomeOtherClass Property5="0" >STRING DATA</SomeOtherClass>
</BaseClass>
<BaseClass d3p1:type="DerivedClass2" Property="PropertyVal2" Property4="456" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance" />
</ItemArray>
</SomeClass>
However, I need to omit the d3p1:type and xmlns:d3p1 attributes and generate an XML structure that looks like this:
<?xml version="1.0" encoding="utf-16"?>
<SomeClass>
<ItemArray>
<BaseClass Property1="PropertyVal1" Property2="123">
<SomeOtherClass Property5="0" >STRING DATA</SomeOtherClass>
</BaseClass>
<BaseClass Property="PropertyVal2" Property4="456" />
</ItemArray>
</SomeClass>
As you can see in the code, I've tried to use XmlType and XmlTypeAttribute, but without success.
Any advice on how can I generate the XML structure as described above (without d3p1:type and xmlns:d3p1 attributes)?
Do you need the subclass elements to be called "BaseClass" - or would the name of the derived class do?
I am serializing similar subclass structures, and also wanted to get rid of the "d3p1:type" and "xmlns:d3p1" tags - instead replacing the "BaseClass" tags with derived class tags. So it is possible to generate xml for your example:
<ItemArray>
<DerivedClass1 Property1="PropertyVal1" Property2="123">
....
</DerivedClass1>
You're using an XmlInclude attribute on your BaseClass to let the serliazer know which derived classes to expect. Instead, you can tell the serializer about the expected subtypes on each element:
public class SomeClass
{
private BaseClass[] _itemArray;
[XmlElement(typeof(DerivedClass1))]
[XmlElement(typeof(DerivedClass2))]
public BaseClass[] ItemArray
{
get { return _itemArray; }
set { _itemArray = value; }
}
Eliminating the "type" attributes. Deserialiazation will also work fine.
You cannot accomplish what you want directly.
If the type being serialized isn't the exact type contained, .NET has to track what the type really is to allow proper deserialization.
To accomplish what you want - use RegEx.Replace() or other post-processing to replace the namespaces.
(e.g. serializing Object which is really a String will cause this problem).
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.Indent = true;
using (XmlWriter writer = XmlWriter.Create(file, settings))
{
XmlSerializer serializer = new XmlSerializer(source.GetType());
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
// ...
}

Storing entity in XML, using MVVM to read/write in WPF Application

Say I've a class (model) called Instance with Properties DatbaseHostname, AccessManagerHostname, DatabaseUsername and DatabasePassword
public class Instance
{
private string _DatabaseHostname;
public string DatabaseHostname
{
get { return _DatabaseHostname; }
set { _DatabaseHostname = value; }
}
private string _AccessManagerHostname;
public string AccessManagerHostname
{
get { return _AccessManagerHostname; }
set { _AccessManagerHostname = value; }
}
private string _DatabaseUsername;
public string DatabaseUsername
{
get { return _DatabaseUsername; }
set { _DatabaseUsername = value; }
}
private string _DatabasePassword;
public string DatabasePassword
{
get { return _DatabasePassword; }
set { _DatabasePassword = value; }
}
}
I'm looking for a sample code to read/write this Model to XML (preferably linq2XML) => storing 1:n instances in XML.
i can manage the the view and ViewModel part myself, although it would be nice if someone had a sample of that part too..
Well, you could use Linq to XML, but your class is a perfect candidate for XML Serialization, which is much simpler IMHO :
var list = new List<Instance>();
...
// Serialization
var xs = new XmlSerializer(typeof(List<Instance>));
using (var writer = XmlWriter.Create(filename))
{
xs.Serialize(writer, list);
}
...
// Deserialization
using (var reader = XmlReader.Create(filename))
{
list = xs.Deserialize(reader) as List<Instance>;
}
Not sure how you want your xml structured, but this should work:
List<Instance> instances = new List<Instance>();
// Get your instances here...
var baseNode = new XElement("Instances");
instances.ForEach(instance => baseNode.Add("Instance",
new XAttribute("DatabaseHostname", instance.DatabaseHostname),
new XAttribute("AccessManagerHostname", instance.AccessManagerHostname),
new XAttribute("DatabaseUsername", instance.DatabaseUsername),
new XAttribute("DatabasePassword", instance.DatabasePassword)));

Categories

Resources