I have a simple "serializable" class defined as follows:
[Serializable]
public class LayoutDetails
{
public bool IsRefreshEnabled { get; set; }
public string GridSettings { get; set; }
public List<string> GroupByPropertyNames { get; set; }
}
The class has been serialized using the following routine:
using (var memoryStream = new MemoryStream())
{
var dc = new DataContractSerializer(obj.GetType());
dc.WriteObject(memoryStream, obj);
memoryStream.Flush();
memoryStream.Position = 0;
StreamReader reader = new StreamReader(memoryStream);
serializedObject = reader.ReadToEnd();
}
I am trying to deserialize it using the following routine:
var bytes = new UTF8Encoding().GetBytes(serializedObject);
using (var stream = new MemoryStream(bytes ))
{
var serializer = new DataContractSerializer(type);
var reader = XmlDictionaryReader.CreateTextReader(stream, XmlDictionaryReaderQuotas.Max);
obj = serializer.ReadObject(reader, true);
}
This worked great; however, now, I want to add a new property to the class (public int? Offset { get; set; }). When I add this property to the class and try to deserialize previously serialized instances (without the property), I get an exception:
Error in line 1 position 148. 'EndElement' 'LayoutDetails' from namespace 'http://schemas.datacontract.org/2004/07/WpfApplication1' is not expected. Expecting element '_x003C_Offset_x003E_k__BackingField'.
My first thought was to implement ISerializable and use a deserialziation ctor, so I tried something like this, but it can't find any members by name - and I've tried several combinations...
public LayoutDetails(SerializationInfo info, StreamingContext context)
{
this.IsRefreshEnabled = (bool)info.GetValue("_x003C_IsRefreshEnabled_x003E_k__BackingField", typeof(bool));
this.IsRefreshEnabled = (bool)info.GetValue("_x003C_IsRefreshEnabled_x003E_k_", typeof(bool));
this.IsRefreshEnabled = (bool)info.GetValue("_x003C_IsRefreshEnabled_x003E", typeof(bool));
this.IsRefreshEnabled = (bool)info.GetValue("x003C_IsRefreshEnabled_x003E", typeof(bool));
this.IsRefreshEnabled = (bool)info.GetValue("IsRefreshEnabled", typeof(bool));
}
Using the DataContractSerializer, how can I add a new member to my class without breaking deserialization on "old" instances?
You can use backing fields for the new properties, and add attribute OptionalField:
[Serializable]
public class LayoutDetails
{
[OptionalField]
private int? offset;
public bool IsRefreshEnabled { get; set; }
public string GridSettings { get; set; }
public List<string> GroupByPropertyNames { get; set; }
public int? Offset
{
get { return offset; }
set { offset = value; }
}
}
if you want to setup a default value, you can add a method to the class with the attribute OnDeserializing:
[OnDeserializing]
private void SetOffsetDefault(StreamingContext sc)
{
offset = 123;
}
Related
how can I get the same object types when I deserialize a json object? The ArrayList is lost and deserialized to an object array and the rectangle is completely lost.
Edit: I cannot change the object types, the payload has to be of type object and I won't know what's in it. I added just 1 example of data contained within but it will vary.
public static void Run()
{
Int32 retval = 0;
ArrayList list = new ArrayList();
list.Add(retval);
list.Add(new Rectangle(1, 1, 1, 1));
Bar bar = new Bar()
{
MessageType = Bar.MessageTypes.Msg1,
Payload = list
};
Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings()
{
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto
};
var json1 = Newtonsoft.Json.JsonConvert.SerializeObject(bar);
var temp1 = Newtonsoft.Json.JsonConvert.DeserializeObject<Bar>(json1);
var json2 = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(bar);
var temp2 = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Bar>(json2);
}
public class Bar
{
public enum MessageTypes
{
Msg1 = 1,
Msg2 = 2
}
public MessageTypes MessageType { get; set; }
public Object Payload { get; set; }
}
It can't deserialize because it is not aware of the types it needs to serialize to.
Don't use an ArrayList, try to specify the properties of your payload . Something like this should work:
public enum MessageTypes
{
Msg1 = 1,
Msg2 = 2
}
public class PayLoad
{
public int Foo { get; set; }
public Rectangle Rectangle { get; set; }
}
public class Bar
{
public MessageTypes MessageType { get; set; }
public PayLoad Payload { get; set; }
}
I decided to use soap serialization instead as it adds the types into the xml
using System;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Soap;
namespace Utils
{
public class XMLUtil
{
public static Byte[] StringToUTF8ByteArray(String xmlString)
{
return new UTF8Encoding().GetBytes(xmlString);
}
public static String SerializeToXML<T>(T objectToSerialize)
{
using (MemoryStream ms = new MemoryStream())
using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8))
{
SoapFormatter soapFormatter = new SoapFormatter();
soapFormatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
soapFormatter.Serialize(ms, objectToSerialize);
String decoded = Encoding.UTF8.GetString(ms.ToArray());
return decoded;
}
}
public static T DeserializeFromXML<T>(string xmlString) where T : class
{
T retval = default(T);
using (MemoryStream stream = new MemoryStream(StringToUTF8ByteArray(xmlString)))
{
SoapFormatter soapFormatter = new SoapFormatter();
soapFormatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
retval = soapFormatter.Deserialize(stream) as T;
}
return retval;
}
}
}
I'm attempting to create a generic method to enable me to parse a CSV document into an object of my choice.
Everything seems to work ok but the results after executing the csv.GetRecords() method are empty and the inner exception of the response is "Instances of abstract classes cannot be created."
I've also tried using the csv.EnumerateRecords(record); and get the same result.
public class ImportManager
{
[Ignore]
public string FileSeperator { get; set; }
[Ignore]
public string Filename { get; set; }
public IEnumerable<T> ParseFile<T>() where T : class
{
using (var reader = new StreamReader(this.Filename))
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = this.FileSeperator;
csv.Configuration.HasHeaderRecord = true;
var results = csv.GetRecords<T>();
return results;
}
}
}
public class MyObject : ImportManager
{
public string Field1 { get; set; }
public DateTime Field2 { get; set; }
public int Field3 { get; set; }
public List<MyObject> LoadFile()
{
var response = ParseFile<MyObject>();
return response.ToList<MyObject>();
}
}
MyObject moObjList= new MyObject() { Filename = "MyFileName.txt", FileSeperator = "|" };
var results = moObjList.LoadFile();
Help!
I believe adding ToList() to csv.GetRecords<T>() may solve your issue. GetRecords<T>() does a lazy load. It doesn't attempt to enumerate the records until you call return response.ToList<MyObject>(); at which time the StreamReader is already disposed.
public class ImportManager
{
[Ignore]
public string FileSeperator { get; set; }
[Ignore]
public string Filename { get; set; }
public IEnumerable<T> ParseFile<T>() where T : class
{
using (var reader = new StreamReader(this.Filename))
using (var csv = new CsvReader(reader))
{
csv.Configuration.Delimiter = this.FileSeperator;
csv.Configuration.HasHeaderRecord = true;
var results = csv.GetRecords<T>().ToList();
return results;
}
}
}
The class I want to store:
[Serializable]
public class Storagee
{
int tabCount;
List<string> tabNames;
List<EachItemListHolder> eachItemsHolder;
public void PreSetting(int count, List<string> strings, List<EachItemListHolder> items)
{
tabCount = count;
tabNames = strings;
eachItemsHolder = items;
}
public void PreSetting(int count ) //debug purpose
{
tabCount = count;
}
public int GetTabCount() { return tabCount; }
public List<string> GetTabNames() { return tabNames; }
public List<EachItemListHolder> GetListEachItemListHolder() { return eachItemsHolder; }
}
Serializing class:
namespace Book
{
class SaveAndLoad
{
public void SaveAll(Storagee str)
{
var path = #"C:\Temp\myserializationtest.xml";
using (FileStream fs = new FileStream(path, FileMode.Create))
{
XmlSerializer xSer = new XmlSerializer(typeof(Storagee));
xSer.Serialize(fs, str);
}
}
public Storagee LoadAll()
{
var path = #"C:\Temp\myserializationtest.xml";
using (FileStream fs = new FileStream(path, FileMode.Open)) //double
{
XmlSerializer _xSer = new XmlSerializer(typeof(Storagee));
var myObject = _xSer.Deserialize(fs);
return (Storagee)myObject;
}
}
}
}
Main method (Window form):
class Book
{
List<EachTab> eachTabs;
Storagee storagee;
SaveAndLoad saveAndLoad;
eachTabs = new List<EachTab>();
storagee = new Storagee();
saveAndLoad = new SaveAndLoad();
void Saving()
{
int count = UserTab.TabCount; // tab counts
storagee.PreSetting(count);
saveAndLoad.SaveAll(storagee);
}
}
It makes xml file but doesn't save data.
I tried the serializing code in different project and it worked.
but it doesn't in this solution
since I'm kind of new to coding I don't know what the problem is
especially serializing part.
serializing codes are copied and pasted with little tweak
It makes xml file but doesn't save data.
It doesn't save any data because your class does not provide any data that it can serialize. XmlSerializer only serializes public fields and properties and the Storagee class doesn't have any.
You could, for example, change your public getter methods to public properties:
public int TabCount { get; set; }
public List<string> TabNames { get; set; }
public List<string> EachItemsHolder { get; set; }
Alternatively, if using public properties is not an option, you could also look into using custom serialization by implementing IXmlSerializable.
I have a plugin-based host application. Its settings are described as a data contract:
[DataContract(IsReference = true)]
public class HostSetup
{
[DataMember]
public ObservableCollection<Object> PluginSetups
{
get
{
return pluginSetups ?? (pluginSetups = new ObservableCollection<Object>());
}
}
private ObservableCollection<Object> pluginSetups;
}
Any plugin has its own settings type. E. g.:
[DataContract(IsReference = true)]
public class Plugin1Setup
{
[DataMember]
public String Name { get; set; }
}
and
[DataContract(IsReference = true)]
public class Plugin2Setup
{
[DataMember]
public Int32 Percent { get; set; }
[DataMember]
public Decimal Amount { get; set; }
}
At run-time, the user has configured host and plugins such a way:
var obj = new HostSetup();
obj.PluginSetups.Add(new Plugin1Setup { Name = "Foo" });
obj.PluginSetups.Add(new Plugin2Setup { Percent = 3, Amount = 120.50M });
Then, my application has saved its settings via DataContractSerializer. Plugin types were passed as known types to the serializer's constructor.
The question.
User physically removes assembly with "Plugin2" and then starts up my application.
So, when the host receives a list of available plugins, it knows nothing about serialized "Plugin2Setup" instance.
I want to ignore this instance, and let the user to work without "Plugin2" settings.
Is there any elegant way to do this?
I can store plugins' settings as data contracts serialized into strings:
public ObservableCollection<String> PluginSetups
but it's not handy and ugly.
Edit 1
The problem is how to deserialize HostSetup instance and ignore serialized Plugin2Setup instance.
Edit 2
My current solution is:
[DataContract(IsReference = true)]
public class PluginSetupContainer
{
[DataMember]
private String typeName;
[DataMember]
private String rawData;
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (SetupParameters != null)
{
using (var writer = new StringWriter())
using (var xmlWriter = new XmlTextWriter(writer))
{
var setupParametersType = SetupParameters.GetType();
var serializer = new DataContractSerializer(setupParametersType);
serializer.WriteObject(xmlWriter, SetupParameters);
xmlWriter.Flush();
typeName = setupParametersType.AssemblyQualifiedName;
rawData = writer.ToString();
}
}
}
[OnSerialized]
private void OnSerialized(StreamingContext context)
{
ClearInternalData();
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
if (!String.IsNullOrEmpty(typeName) && !String.IsNullOrEmpty(rawData))
{
var setupParametersType = Type.GetType(typeName, false);
if (setupParametersType != null)
{
using (var reader = new StringReader(rawData))
using (var xmlReader = new XmlTextReader(reader))
{
var serializer = new DataContractSerializer(setupParametersType);
SetupParameters = serializer.ReadObject(xmlReader);
}
}
ClearInternalData();
}
}
private void ClearInternalData()
{
typeName = null;
rawData = null;
}
public Object SetupParameters { get; set; }
}
[DataContract(IsReference = true)]
public class HostSetup
{
[DataMember]
public ObservableCollection<PluginSetupContainer> PluginSetups
{
get
{
return pluginSetups ?? (pluginSetups = new ObservableCollection<PluginSetupContainer>());
}
}
private ObservableCollection<PluginSetupContainer> pluginSetups;
}
May be it's terrible, but it works. :)
I think ideally you should have something on the lines of
[DataContract(IsReference = true)]
[MyPluginCustomAttribute]
public class Plugin1Setup
{
}
and when you application loads you should initialize obj.PluginSetups using reflection based on MyPluginCustomAttribute so only assemblies that are present have their types registered. So you won't have the problem of missing assemblies. You can also use Managed Extensibility Framework (MEF) instead of your own MyPluginCustomAttribute
I defined two classes. First one...
[Serializable]
public class LocalizationEntry
{
public LocalizationEntry()
{
this.CatalogName = string.Empty;
this.Identifier = string.Empty;
this.Translation = new Dictionary<string, string>();
this.TranslationsList = new List<Translation>();
}
public string CatalogName
{
get;
set;
}
public string Identifier
{
get;
set;
}
[XmlIgnore]
public Dictionary<string, string> Translation
{
get;
set;
}
[XmlArray(ElementName = "Translations")]
public List<Translation> TranslationsList
{
get
{
var list = new List<Translation>();
foreach (var item in this.Translation)
{
list.Add(new Translation(item.Key, item.Value));
}
return list;
}
set
{
foreach (var item in value)
{
this.Translation.Add(item.Language, item.Text);
}
}
}
}
...where public List<Translation> TranslationsList is a wrapper for non-serializable public Dictionary<string, string> Translation.
Pair of key and value is defined as follows:
[Serializable]
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language
{
get;
set;
}
[XmlText]
public string Text
{
get;
set;
}
public Translation()
{
}
public Translation(string language, string translation)
{
this.Language = language;
this.Text = translation;
}
}
At last code used to serialize:
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
};
entry.Translation.Add("PL", "jabłko");
entry.Translation.Add("EN", "apple");
entry.Translation.Add("DE", "apfel");
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
}
LocalizationEntry deserializedEntry;
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
}
}
The problem is that after deserialization deserializedEntry.TranslationsList is empty. I set a breakpoint at setter of LocalizationEntry.TransalionsList and it comes from deserializer empty as well. Product of serialization is of course valid. Is there any gap in my code?
EDIT:
Here is generated XML:
<?xml version="1.0"?>
<LocalizationEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CatalogName>Catalog</CatalogName>
<Identifier>Id</Identifier>
<Translations>
<Translation lang="PL">jabłko</Translation>
<Translation lang="EN">apple</Translation>
<Translation lang="DE">apfel</Translation>
</Translations>
</LocalizationEntry>
The problem is that your TranslationList property is not being set by the Xml Deserializer. The set method will be hit but only by the call to this.TranslationsList = new List(); in the LocalisationEntry constructor. I'm not yet sure why but I suspect it's because it doesn't know how to convert an array of Translation objects back into a List.
I added the following code and it worked fine:
[XmlArray(ElementName = "Translations")]
public Translation[] TranslationArray
{
get
{
return TranslationsList.ToArray();
}
set
{
TranslationsList = new List<Translation>(value);
}
}
[XmlIgnore]
public List<Translation> TranslationsList
....
I am guessing the problem has to do with this:
public List<Translation> TranslationsList
The get/set operators are designed only for something to get or assign a fully-formed list. If you tried to use this in your own code, for example, every time you would do something like
TranslationsList.Add(item)
It would just create a new list from the existing dictionary and not actually deal with your item. I bet the deserializer works much the same way: uses set to create the new object once, then uses get as it adds each item from the XML. Since all that happens in get is it copies from the dictionary (which is empty when you begin your deserialization) you end up with nothing.
Try replacing this with just a field:
public List<Translation> TranslationsList;
and then explicitly call the code to copy the dictionary to this list before you serialize, and copy it from this list to the dictionary after you deserialize. Assuming that works, you can probably figure out a more seamless way to implement what you're trying to do.
I've created a sample, which will allow you to avoid the unnecessary hidden property when using the XmlSerializer:
class Program
{
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
Translations =
{
{ "PL", "jabłko" },
{ "EN", "apple" },
{ "DE", "apfel" }
}
};
using (MemoryStream stream = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
stream.Seek(0, SeekOrigin.Begin);
LocalizationEntry deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
serializer.Serialize(Console.Out, deserializedEntry);
}
}
}
public class LocalizationEntry
{
public LocalizationEntry() { this.Translations = new TranslationCollection(); }
public string CatalogName { get; set; }
public string Identifier { get; set; }
[XmlArrayItem]
public TranslationCollection Translations { get; private set; }
}
public class TranslationCollection
: Collection<Translation>
{
public TranslationCollection(params Translation[] items)
{
if (null != items)
{
foreach (Translation item in items)
{
this.Add(item);
}
}
}
public void Add(string language, string text)
{
this.Add(new Translation
{
Language = language,
Text = text
});
}
}
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language { get; set; }
[XmlText]
public string Text { get; set; }
}
There are some drawbacks when working with the XmlSerializer class itself. The .NET guidelines encourage you the not provide public-setters for collection-properties (like your translation list). But when you look at the code generated by the XmlSerializer, you'll see that it will use the Setter regardless of it is accessible. This results in a compile-error when the interim class is dynamically loaded by the XmlSerializer. The only way to avoid this, is to make the XmlSerializer think, that it can't actually create an instance of the list and thus won't try to call set for it. If the XmlSerializer detects that it can't create an instance it will throw an exception instead of using the Setter and the interim class is compiled successfully. I've used the param-keyword to trick the serializer into thinking that there is no default-constructor.
The only drawback from this solution is that you have to use a non-generic, non-interface type for the property (TranslationCollection) in my example.