I'm having trouble with deserializing a list of objects. For some reason only the first object in the serialized list is returned from the load method. I've looked at numerous questions that seem related, but haven't been able to find a solution to my specific problem. I think it may have something to do with the private backing collection I'm using? Any help would be appreciated. (class names and methods have been made generic)
Here's my deserialization method:
public static ObjectList LoadLibrary()
{
BinaryFormatter formatter = new BinaryFormatter();
FileStream loadStream;
string path = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "ObjectList.bin");
ObjectList lib = null;
if (!File.Exists(path))
{
lib = new ObjectList();
return lib;
}
try
{
using (loadStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
lib = (ObjectList)formatter.Deserialize(loadStream);
}
}
catch (Exception e)
{
MessageBox.Show("Object list could not be loaded. Error: \n" + e);
}
return lib;
}
Here's my ObjectList class;
[Serializable]
public class ObjectList: IEnumerable<Obj>
{
#region Construction
/// <summary>
/// Contracts Test Case Library Constructor
/// </summary>
public ObjectList() { }
/// <summary>
/// Private backing collection
/// </summary>
private List<Obj> _objectList = new List<Obj>();
#endregion
And for good measure, here's my Obj class:
[Serializable]
public class Obj
{
#region Constructors
public Obj(string Name, string Description)
{
this.Name = Name;
this.Description = Description;
}
public Obj() { }
#endregion
Related
In my follow second line, I found a convert error argument:
UserStackVM _listeStack = JsonWorker.ReadData();
ListeStacks = new ObservableCollection<UserStackVM>(_listeStack); // here
My error is:
cannot convert from 'MyStack.ViewModels.UserStackVM' to
'System.Collections.Generic.List'
UserStackVM is a ViewModel:
#region Properties
private string name;
...
private string[] path;
...
#endregion
JsonWorker is a static class using Json.NET (http://www.newtonsoft.com/json):
#region Properties
private static string _json;
private static UserStackVM _userStack;
#endregion
#region Methods
/// <summary>
/// Open the json config file. Create it if he doesn't exist.
/// </summary>
private static void OpenFile()
{
using (var stream = new FileStream(#"config.json", FileMode.OpenOrCreate))
{
using (var reader = new StreamReader(stream))
{
_json = reader.ReadToEnd();
}
}
}
/// <summary>
/// Read the json config file and return all data.
/// </summary>
/// <returns></returns>
public static UserStackVM ReadData()
{
OpenFile();
_userStack = JsonConvert.DeserializeObject<UserStackVM>(_json);
return _userStack;
}
#endregion
Per advance, thanks for your help.
'MyStack.ViewModels.UserStackVM' to 'System.Collections.Generic.List'
That ObservableCollection(T) Constructor expects a List<T> (list of instances); you are only providing a single instance. Change it to
UserStackVM _listeStack = JsonWorker.ReadData();
ListeStacks = new ObservableCollection<UserStackVM>();
ListeStacks.Add( _listeStack );
or
ListeStacks = new ObservableCollection<UserStackVM>
( new List<UserStackVM> () { listeStack } );
I am creating an android app in VS2012 using Xamarin.Android. I am displaying a custom list in Main screen. I need to pass a custom object(with ID,String,String,String properties) from this Main activity to another when user clicks on list item.
Can anyone please help me with some example?
edit:
I have already tried solution mention in other question
but the problem is I am getting below exception:
This is how I am extracting in second activity
InsuranceReminderBO i = (InsuranceReminderBO)Intent.GetSerializableExtra("SelectedItemID");
i is null
and in first activity setting it like this:
Intent intent = new Intent(this, typeof(ReminderDetails));
intent.PutExtra("SelectedItemID", selectedInsurance);
StartActivity(typeof(ReminderDetails));
where class InsuranceReminderBO is defined as
public class InsuranceReminderBO : Java.Lang.Object, Java.IO.ISerializable
I have also tried using IParcelable but in that I got error Creator is not defined
in ICreator or Creator
Following the implementation of Iparcelable on CustomObject
'public class InsuranceReminderBO : Java.Lang.Object, IParcelable
{
public InsuranceReminderBO()
{
}
#region Objects and Properties
private int id;
private String strCompanyName;
private String strPremiumAmount;
private String stDueDate;
public int ID
{
get { return this.id; }
set { this.id = value; }
}
public String Company_Name
{
get { return this.strCompanyName; }
set { this.strCompanyName = value; }
}
public String Premium_Amount
{
get { return this.strPremiumAmount; }
set { this.strPremiumAmount = value; }
}
public String Due_Date
{
get { return this.stDueDate; }
set { this.stDueDate = value; }
}
#endregion
#region IParcelable implementation
// The creator creates an instance of the specified object
private static readonly GenericParcelableCreator<InsuranceReminderBO> _creator
= new GenericParcelableCreator<InsuranceReminderBO>((parcel) => new InsuranceReminderBO(parcel));
[ExportField("CREATOR")]
public static GenericParcelableCreator<InsuranceReminderBO> GetCreator()
{
return _creator;
}
// Create a new SelectListItem populated with the values in parcel
private InsuranceReminderBO(Parcel parcel)
{
ID = parcel.ReadInt();
Company_Name = parcel.ReadString();
Premium_Amount = parcel.ReadString();
Due_Date = parcel.ReadString();
}
public int DescribeContents()
{
return 0;
}
// Save this instance's values to the parcel
public void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
dest.WriteInt(ID);
dest.WriteString(Company_Name);
dest.WriteString(Premium_Amount);
dest.WriteString(Due_Date);
}
// Closest to the 'Java' way of implementing the creator
/*public sealed class SelectListItemCreator : Java.Lang.Object, IParcelableCreator
{
public Java.Lang.Object CreateFromParcel(Parcel source)
{
return new SelectListItem(source);
}
public Java.Lang.Object[] NewArray(int size)
{
return new SelectListItem[size];
}
}*/
#endregion
}
#region GenericParcelableCreator
/// <summary>
/// Generic Parcelable creator that can be used to create objects from parcels
/// </summary>
public sealed class GenericParcelableCreator<T> : Java.Lang.Object, IParcelableCreator
where T : Java.Lang.Object, new()
{
private readonly Func<Parcel, T> _createFunc;
/// <summary>
/// Initializes a new instance of the <see cref="ParcelableDemo.GenericParcelableCreator`1"/> class.
/// </summary>
/// <param name='createFromParcelFunc'>
/// Func that creates an instance of T, populated with the values from the parcel parameter
/// </param>
public GenericParcelableCreator(Func<Parcel, T> createFromParcelFunc)
{
_createFunc = createFromParcelFunc;
}
#region IParcelableCreator Implementation
public Java.Lang.Object CreateFromParcel(Parcel source)
{
return _createFunc(source);
}
public Java.Lang.Object[] NewArray(int size)
{
return new T[size];
}
#endregion
}
#endregion'
I am putting object in intent as
InsuranceReminderBO selectedInsurance = listOfInsurance[e.Position];
Intent intent = new Intent(this, typeof(ReminderDetails));
intent.PutExtra("SelectedItem", selectedInsurance);
And reading in second activity as
InsuranceReminderBO i = (InsuranceReminderBO)Intent.GetParcelableExtra("SelectedItem");
but getting i as null.
To coat tail on the servicestack.text solution, you can just download the android DLL's and reference them into your solution. You can use this and add it to your solution, build it separately, as alternatives. https://github.com/ServiceStack/ServiceStack.Text/tree/master/src/ServiceStack.Text.Android
Also I use a couple of methods to convert items back and forth that may be helpful, try
static public string ToJSON(this object item)
{
var myval = JsonSerializer.SerializeToString(item);
return myval;
}
static public T FromJSON<T>(string code)
{
var item = JsonSerializer.DeserializeFromString<T>(code);
return item;
}
There's an article on using IParcelable in Xamarin here.
Personally, I've always just serialised to JSON, passed a string extra and deserialised it on the other end.
If you're using ServiceStack.Text, which I like, you can do something like this:
intent.PutExtra("SelectedItemId", JsonSerializer.SerializeToString(selectedInsurance));
And on the other end:
var insurance = JsonSerializer.DeserializeFromString<InsuranceReminderBO>(Intent.GetStringExtra("SelectedItemId"))
No need to implement Java.Lang.Object, Java.IO.ISerializable
After doing a lot of search on Google finally i found a solution here.
Basically I used StartActivity(intent);
I have a base class defined as follows:
public abstract class XMLBackedObject<T> where T: XMLBackedObject<T>
{
/// <summary>
/// Load the specified xml file and deserialize it.
/// </summary>
/// <param name='filePath'>
/// File path to load
/// </param>
public static T Load(string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using(FileStream stream = new FileStream(filePath, FileMode.Open))
{
return serializer.Deserialize(stream) as T;
}
}
/// <summary>
/// Save this instance to the specified file path
/// </summary>
/// <param name='filePath'>
/// File path to save to.
/// </param>
public void Save(string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using(FileStream stream = new FileStream(filePath, FileMode.Create))
{
serializer.Serialize(stream, this);
}
}
}
And classes inherit it as follows:
Config.cs:
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public class Config : XMLBackedObject<Config>
{
public Config()
{
}
public string WordDirectoryPath;
public string CommandDirectoryPath;
}
Command.cs:
using UnityEngine;
using System.Collections.Generic;
[System.Serializable]
public abstract class Command : XMLBackedObject<Command>
{
//The word that triggers this command
public Word Word;
//The command's target
public List<Word> Targets;
//Minimum number of targets for the command to be valid
public int RequiredTargets;
//Message to send when bad targets are supplied
public string BadTargetString;
//Message to send when no target is supplied
public string noTargetString;
public Command(Word word, List<Word> targets,int requiredTargets)
{
Targets = targets;
this.Word = word;
this.RequiredTargets = requiredTargets;
}
public Command()
{
Targets = new List<Word>();
}
/// <summary>
/// Execute the command on the supplied targets
/// </summary>
/// <param name='targets'>
/// Targets to process
/// </param>
public abstract void Execute(IEnumerable<Word> targets);
}
MenuNavigationCommand.cs:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class MenuChoiceCommand : Command {
public MenuChoiceCommand()
{
}
public MenuChoiceCommand(Word word, List<Word> targets, int requiredTargets) : base(word,targets,requiredTargets)
{
}
public override void Execute (System.Collections.Generic.IEnumerable<Word> targets)
{
}
}
And this is the code that calls the Save functions:
public void BuildTestXMLFiles()
{
Config config = new Config();
config.CommandDirectoryPath = "commdirpath";
config.WordDirectoryPath = "wordirparth";
config.Save (Application.dataPath + "/testconfig.xml");
MenuChoiceCommand command = new MenuChoiceCommand(word,new List<Word>(),2);
command.Targets.Add (word);
command.Save (Application.dataPath + "/testcommand.xml");
}
Config's Save function executes without any hitches, but using Save on MenuNavigationCommand gives me this error:
InvalidOperationException: The type of the argument object 'MenuChoiceCommand' is not primitive.
All I need MenuNavigationCommand to do is save the fields that exist in the Command class it inherits from, not any new fields in MenuNavigationCommand. Is there any way to do this? Or should I just implement a Load and Save method on every class that uses more than one level of inheritance?
EDIT: Added the full source for the files.
MenuChoiceCommand inherits Command, which inherits XMLBackedObject<Command>, not XMLBackedObject<MenuChoiceCommand>. So the serializer created by Save is for type Command, not MenuChoiceCommand... You would need to make MenuChoiceCommand inherit XMLBackedObject<MenuChoiceCommand> for this to work (but then you wouldn't be able to make it inherit Command, since C# doesn't allow multiple inheritance).
Using the curiously recurring template pattern for this might seem a good idea at first glance, but as you can see, you can quickly encounter its limitations.
Anyway, I don't think the serialization logic should be part of the data class itself; it would probably be better to do it in a helper class with generic methods:
public static class XmlHelper
{
public static T Load<T>(string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using(FileStream stream = new FileStream(filePath, FileMode.Open))
{
return (T)serializer.Deserialize(stream);
}
}
public static void Save<T>(T obj, string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using(FileStream stream = new FileStream(filePath, FileMode.Create))
{
serializer.Serialize(stream, obj);
}
}
}
In PHP it's common practice to pass an array as options for a class, and then merge that array with a set another array that holds the defaults.
Something like this.
class MyObject
{
private static $defaults = array('value'=>10);
private $settings;
public function Something(array $settings = array())
{
$this->settings = array_merge(static::defaults,$settings);
}
}
You can do the something in JavaScript using jQuery or other libraries that introduce the merge function. These scripts let you take two Javascript objects and merge them together. Allowing you to use one as the defaults, and another to override those defaults.
I've found this pattern very useful, because it allows you to configure a large set of defaults but only assign the settings you need.
Is there anyway to do something like this in C#?
I could write a function that uses reflection to do this on public properties, but I was thinking something like this must have already been done.
EDIT:
This question has been asked before on stack, but not answered in a way that provides the same simplicity as what can be done in PHP and Javascript.
I wasn't able to find an answer that did exactly what I wanted. So I wrote a small method to do this. It'll take two objects of the same time, and merge their fields/properties assuming that a null value represents an unassigned field/property.
Here is the usage example. Create a class to hold options for a communications class, let the communication class have defaults and then initialize the communication with user settings.
An example settings class.
public class ComSettings
{
public int? Port;
public string? Address;
public bool? KeepAlive;
}
An example class that uses those settings in the constructor.
public class ComLibrary
{
private static ComSettings _defaults = new ComSettings { Port = 80, Address = "localhost" };
protected ComSettings settings;
public ComLibrary(ComSettings pSettings)
{
this.settings = ObjectMerge<ComSettings>(_defaults, pSettings);
}
}
This will let different classes use the ComSettings but each could have different defaults. The only limitation is that the field/properties have to support null assignments.
Here's the implementation of ObjectMerge.
/// <summary>
/// Creates a new object that contains the properties of the two objects merged together.
/// </summary>
/// <typeparam name="T">The class type to merge.</typeparam>
/// <param name="pDefaults">Instance of the defaults object.</param>
/// <param name="pSettings">Instance of the settings object.</param>
/// <returns>A new instance of T with the merged results.</returns>
public static T ObjectMerge<T>(T pDefaults, T pSettings, bool pMergeFields = true, bool pMergeProperties = true) where T : class, new()
{
T target = new T();
Type type = typeof(T);
List<MemberInfo> infos = new List<MemberInfo>(type.GetMembers());
foreach (MemberInfo info in infos)
{
// Copy values from either defaults or settings
if (pMergeFields && info.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)info;
if (field.IsPublic)
{
object value = field.GetValue(pSettings);
value = (value == null) ? field.GetValue(pDefaults) : value;
field.SetValue(target, value);
}
}
// Copy values from either defaults or settings
if (pMergeProperties && info.MemberType == MemberTypes.Property)
{
PropertyInfo prop = (PropertyInfo)info;
if (prop.CanWrite && prop.CanRead)
{
object value = prop.GetValue(pSettings, null);
value = (value == null) ? prop.GetValue(pDefaults, null) : value;
prop.SetValue(target, value, null);
}
}
}
return target;
}
And here is a simple unit test.
/// <summary>
///This is a test class for CoreUtilsTest and is intended
///to contain all CoreUtilsTest Unit Tests
///</summary>
[TestClass()]
public class CoreUtilsTest
{
/// <summary>
/// A class to perform testing on.
/// </summary>
public class MyClassA
{
public string Param1;
public string Param2;
public string Param3;
}
/// <summary>
/// A class to perform testing on.
/// </summary>
public class MyClassB
{
private string _param1;
public string Param1
{
get { return _param1; }
set { _param1 = value; }
}
private string _param2;
public string Param2
{
get { return _param2; }
set { _param2 = value; }
}
private string _param3;
public string Param3
{
get { return _param3; }
set { _param3 = value; }
}
}
/// <summary>
///A test for SetProperties
///</summary>
[TestMethod()]
public void Merging_Fields()
{
MyClassA defaults = new MyClassA { Param1 = "defaults" };
MyClassA settings = new MyClassA { Param2 = "settings" };
MyClassA results = CoreUtils.ObjectMerge<MyClassA>(defaults, settings);
Assert.AreEqual("defaults", results.Param1);
Assert.AreEqual("settings", results.Param2);
Assert.AreEqual(null, results.Param3);
}
[TestMethod()]
public void Merging_Properties()
{
MyClassB defaults = new MyClassB { Param1 = "defaults" };
MyClassB settings = new MyClassB { Param2 = "settings" };
MyClassB results = CoreUtils.ObjectMerge<MyClassB>(defaults, settings);
Assert.AreEqual("defaults", results.Param1);
Assert.AreEqual("settings", results.Param2);
Assert.AreEqual(null, results.Param3);
}
}
I've problems to serialize and deserialize a singleton via DataContract.
First some facts:
1.) Singleton is "internal"
2.) Singleton contains Dictionaries
My serialization and deserialization works fine, but it isn't the right way for a singleton. If I deserialize the xml, I always generate a new instance of my singleton and overwrite the current reference of the singleton object - but after this it isn't a singleton further.
Has anybody any idea? - Thanks.
Check this link from msdn, there is an example on serializing a singleton. After deserialization you should return the reference and not the object.
Try using NetDataContractSerializer-
The NetDataContractSerializer differs from the DataContractSerializer
in one important way: the NetDataContractSerializer includes CLR type
information in the serialized XML, whereas the DataContractSerializer
does not. Therefore, the NetDataContractSerializer can be used only if
both the serializing and deserializing ends share the same CLR types.
The serializer can serialize types to which either the
DataContractAttribute or SerializableAttribute attribute has been
applied. It also serializes types that implement ISerializable.
Code example:
[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
class Person : IExtensibleDataObject
{
[DataMember()]
public string FirstName;
[DataMember]
public string LastName;
[DataMember()]
public int ID;
public Person(string newfName, string newLName, int newID)
{
FirstName = newfName;
LastName = newLName;
ID = newID;
}
private ExtensionDataObject extensionData_Value;
public ExtensionDataObject ExtensionData
{
get { return extensionData_Value; }
set { extensionData_Value = value; }
}
}
Searialization:
Person p1 = new Person("Zighetti", "Barbara", 101);
FileStream fs = new FileStream(fileName, FileMode.Create);
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs);
NetDataContractSerializer ser = new NetDataContractSerializer();
ser.WriteObject(writer, p1);
Msdn Link here
(code for .net 4.0)
I had the same problem:
the de-serialization needs to create a new instance of the singleton class, which it can(!) do because its inside a member function: the constructor is visible to members, but that instance cannot replace the singleton instance visible from the outside (the "this").
So you have to copy the properties from your de-serialized instance into the "this" instance.
Doing the copying by hand gets old fast, so here is my solution using reflection to copy the public writable members that are not marked [xmlignore]:
public static class SerializationHelpers
{
/// <summary>
/// Copy all public props and fields that are not xmlignore
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="target"></param>
/// <param name="other"></param>
public static void CopyTypeFields<T>(T target, T other)
{
// get all public static properties of MyClass type
PropertyInfo[] propertyInfos = other.GetType().GetProperties(BindingFlags.Public
| BindingFlags.Instance);
FieldInfo[] fis = other.GetType().GetFields(BindingFlags.Public |
BindingFlags.Instance);
foreach (FieldInfo fi in fis)
{
if ((fi.Attributes & FieldAttributes.FieldAccessMask) !=
FieldAttributes.Literal &&
(fi.Attributes & FieldAttributes.FieldAccessMask) !=
FieldAttributes.Static)
{
if (IsXmlIgnored(fi)) { continue; }
var myval = fi.GetValue(other);
fi.SetValue(target, myval);
}
}
foreach (PropertyInfo pi in propertyInfos)
{
if (!pi.CanWrite || !pi.CanRead) { continue; }
if (IsXmlIgnored(pi)) { continue; }
var myval = pi.GetValue(other, null);
pi.SetValue(target, myval, null);
}
}
private static bool IsXmlIgnored(MemberInfo pi)
{
object[] fiGetCustomAttributes = pi.GetCustomAttributes(false);
foreach (object ob in fiGetCustomAttributes)
{
if (ob.GetType().
Equals(typeof(System.Xml.Serialization.XmlIgnoreAttribute)))
{
return true;
}
}
return false;
}
}
// to use it ...
// the deserialization method of the singleton mySingleton
public bool loadSingleton()
{
bool ret= false;
try
{
Type myType = GetType();
XmlSerializer reader = new XmlSerializer(myType);
try
{
using (StreamReader file = new StreamReader(filename))
{
try
{
mySingleton t1 = (mySingleton)reader.Deserialize(file);
CopySerializationFields(t1);
ret= true;
}
catch
{
...
}
}
}
catch
{
...
}
}
catch (Exception ex)
{
...
}
return ret;
}
private void CopySerializationFields(ProcessingSettings other)
{
SerializationHelpers.CopyTypeFields(this, other);
}
I just searched quite long for a similar solution for atomic classes and have found an answer to a similar problem by Marc Gravell. This also works for singletons.
In your singleton class you implement the GetRealObject method defined by the interface System.Runtime.Serialization.IObjectReference. There you could also add previously serialized data to the singleton if needed and return the static reference as the reference that is used after deserialization.
Here's my example:
[System.Runtime.Serialization.DataContract]
public class MySingletonClass : System.Runtime.Serialization.IObjectReference
{
private MySingletonClass()
{
}
private static MySingletonClass _Instance;
public static MySingletonClass Instance
{
get
{
if (_Instance == null)
_Instance = new MySingletonClass();
return _Instance;
}
}
object System.Runtime.Serialization.IObjectReference.GetRealObject(System.Runtime.Serialization.StreamingContext context)
{
MySingletonClass realObject = Instance;
realObject.Merge(this);
return realObject;
}
private void Merge(MySingletonClass otherInstance)
{
// do your merging here
}
}
You can use this for atomic classes too. You only have to change the Instance property to a GetInstance method and call it with the appropriate property in the GetRealObject method (i.e. an ID).
[DataContract]
public sealed class SerializableSingletonPattern
{
public static SerializableSingletonPattern Instance { get; private set; } = new SerializableSingletonPattern();
[DataMember] public bool YourData { get; private set; }
// explicit static constructor so C# compiler will not mark type as beforefieldinit
static SerializableSingletonPattern() { }
SerializableSingletonPattern() // your constructor
{
}
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
Instance = this;
}
}