I am trying to serialize some data which contains an observable collection of objects and write it into a text tile. My output is [] and I do now know where I have made a mistake.
my object code
public class ObjectList : ObservableCollection<string>, INotify...
{
public ObservableCollection<string> ObjectListInstance = new ObservableCollection<string>();
public string Name;
... get set methods & property changed method
}
my IO code
using (Stream newStream = await Windows.Storage.ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync("file.txt", Windows.Storage.CreationCollisionOption.ReplaceExisting))
{
DataContractJsonSerializer newDataContractJsonSerializer = new DataContractJsonSerializer(typeof(ObservableCollection<ObjectList>));
newDataContractJsonSerializer.WriteObject(newStream, ObjectList);
}
my code stub
ObjectList newObjectList = new ObjectList();
newObjectList.Name = "AAA NAME";
newObjectList.ObjectListInstance.Add("ITEM 1");
newObjectList.ObjectListInstance.Add("ITEM 2");
bool status = await IOClass.IO.WriteCategory(newObjectList);
You can't have a class that is both a collection and has additional properties to be serialized. However, you can have a class that contains a list and has additional properties, which is what I think you are trying to do here. To make it work you will need to make some adjustments to your code:
Your ObjectList should not inherit from ObservableCollection<T> (or any other list type)
You must mark your class with [DataContract], and mark the properties/fields you want to be serialized with [DataMember].
When you create your DataContractJsonSerializer instance, pass to the constructor the top-level type you want to serialize. In your case, this should be ObjectList, not ObservableCollection<ObjectList>.
When you call WriteObject on the serializer, the second parameter should be the object instance you are serializing, not a Type.
Here is the corrected class:
[DataContract]
public class ObjectList
{
[DataMember]
public ObservableCollection<string> ObjectListInstance = new ObservableCollection<string>();
[DataMember]
public string Name;
}
Here is the corrected serialization code:
DataContractJsonSerializer newDataContractJsonSerializer =
new DataContractJsonSerializer(typeof(ObjectList));
newDataContractJsonSerializer.WriteObject(newStream, newObjectList);
With these changes you should get the following JSON output:
{"Name":"AAA NAME","ObjectListInstance":["ITEM 1","ITEM 2"]}
Related
This question already has answers here:
How do I get json.net to serialize members of a class deriving from List<T>?
(3 answers)
Closed 6 years ago.
I can't find a way to serialize with JSON.NET a list of derived lists, and I'm starting to wonder if it's even possible. Let me explain the situation with some code.
I've created a new class which is derived from a list of a specific object (tried first with a generic one) and added a string property NomGroupe:
public class GroupeActes : List<Acte>
{
public string NomGroupe { get; set; }
public GroupeActes(string nom, List<Acte> liste)
{
NomGroupe = nom;
foreach (var acte in liste)
{
this.Add(acte);
}
}
}
Then, in my code, I've declared a list of this class (List<GroupeActes> listOfGroupeActes) and I fill it with data. For the serialization, I use this code:
JsonSerializer serializer = new JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.All;
using (StreamWriter sw = new StreamWriter(#"listOfGroupeActes.json"))
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, listOfGroupeActes);
}
I've tried with and without the TypeNameHandling.All parameter and with several combination of Json.net properties and even with DataContract/DataMember.
So far, I only managed to get in my json file either the data of each nested List<Acte> without the NomGroupe property, or the other way around. But not both, which is what I'd like to have.
Two questions then:
Is it even possible?
If yes, how can I do it?
Thanks for your help!
You don't want to inherit from List<T>.
Create a list property instead:
public class GroupeActes
{
public List<Acte> Actes { get; set; }
public string NomGroupe { get; set; }
public GroupeActes(string nom, List<Acte> liste)
{
NomGroupe = nom;
Actes.AddRange(acte);
}
}
Lists (and other collection types) get special treatment while serializing. You don't want the collection type's public properties (such as Capacity and Count) in your output, so the property you added through inheritance won't be serialized either.
A collection is serialized like this:
if o is IEnumerable
foreach object s in o
serialize o
So the serializer won't even look at your enumerable's properties.
Try to use Newtonsoft Json.NET
string itemToSend = JsonConvert.SerializeObject(dataModel);
I'm simply trying to use Serialization properties to temporary store datas in a string. I tested many method and those functions are the ones I could use (since in my real classes I have ObjectId, a lot of serialization classes don't work).
However, even with a simple test it doesn't work, my deserialization is null:
public class MyClass
{
public string test = "bob";
}
static public void function()
{
MyClass test = new MyClass();
string data = Newtonsoft.Json.JsonConvert.SerializeObject(test);
object testb = Newtonsoft.Json.JsonConvert.DeserializeObject(data);
MyClass testa = Newtonsoft.Json.JsonConvert.DeserializeObject(data) as MyClass;
}
Results are (debugger):
datab : { "test": "bob"}
testa is null.
Why? How can I convert an object like testb with keys and value to my correct type?
Problem is the way you are type casting.
Try out this one and it should work just fine
MyClass testa = Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass>(data);
That shall be all.
Use the generic de-serialise method:
MyClass testa = Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass>(data);
You should define your classes with public getters and setters:
public class MyData
{
public string Name {get; set;}
}
Then, create an instance of the class and serialize it:
var data = new MyData() { Name = "bob" };
var serialized = JsonConvert.SerializeObject(data);
Console.WriteLine(serialized);
When you deserialize, you can use DeserializeObject<T> to tell JSON.NET which type to deserialize back to:
var deserialized = JsonConvert.DeserializeObject<MyData>(serialized);
Console.WriteLine(deserialized.Name);
Live fiddle: https://dotnetfiddle.net/w4B1IK
The mongo C# driver version I am using is 1.1. I have the code structured like shown below.
public abstract Class BaseClass
{
public int BCProp {get; set;}
}
public class DerivedClass1 : BaseClass
{
public int DCProp1 {get; set;}
}
public class DerivedClass2 : BaseClass
{
public int DCProp2 {get; set;}
}
public class ClassOfInterest
{
public int Prop1 {get; set;}
// I want to bring back only certain values
// from the elements in this array while deserializing
public BaseClass[] ElementArray {get; set;}
}
Before inserting the documents into MongoDB, I use BsonClassMap to Register the classes and set the discriminator as Class Name with Namespace. So when I create an object of ClassOfInterest and the ElementArray is an array of type DerivedClass1 elements, when I insert it into the DB, the array elements have "_t" as "DerivedClass1". All that looks good according to the documentation about Polymorphic classes and Discriminators.
For some reason, I decided to deserialize only some of the properties of ClassOfInterest. I do not want to deserialize Prop1 and I want just the ElementArray, so I wrote code like this
// Here I am specifying that I am interested only in ElementArray
// A Element in ElementArray will be of type DerivedClass1 and will
// include both BCProp and DCProp1
FieldsBuilder _fb = new FieldsBuilder();
_fb.Include("ElementArray");
List<string> IncludedFields = new List<string>();
var dic = _fb.ToBsonDocument().ToDictionary();
IncludedFields.AddRange(dic.Keys.ToList());
// I am querying DB
MongoCollection<ClassOfInterest> mcoll = ActiveDb.GetCollection<ClassOfInterest>(COICollName);
List<ClassOfInterest> COIObjects = mcoll.FindAll().SetFields(IncludedFields.ToArray()).ToList();
The above works fine. The returned objects have only ElementArray and do not include Prop1. The discriminator worked and the returned objects have Elements of type DerivedClass1 in ElementArray.
Again, for some reason, I do not want to deserialize everything from DerivedClass1. So I do the below.
// Notice that I want to get back only BCProp in all ElementArray
FieldsBuilder _fb = new FieldsBuilder();
_fb.Include("ElementArray.BCProp");
List<string> IncludedFields = new List<string>();
var dic = _fb.ToBsonDocument().ToDictionary();
IncludedFields.AddRange(dic.Keys.ToList());
// I am querying DB
MongoCollection<ClassOfInterest> mcoll = ActiveDb.GetCollection<ClassOfInterest>(COICollName);
List<ClassOfInterest> COIObjects = mcoll.FindAll().SetFields(IncludedFields.ToArray()).ToList();
This time however, I get the error "Instances of abstract classes cannot be created"
What went wrong this time? If I ask for the whole ElementArray, it properly deserializes the Elements in Element Array to DerivedClass1. However, when I ask for specific property (Belongs to base class), I get the error.
I was not asking for discriminator, so the deserializer had no idea how to deal with it and tried to create object of abstract class.
Instead of doing just
_fb.Include("ElementArray.BCProp")
I did
_fb.Include("ElementArray.BCProp");
_fb.Include("ElementArray._t");
It works now.
I want to serialize a MyClass, which is a class that contains a list MyClass.
In the XML, I want to write only myClass.Name, then when I deserialize it, I then find which MyClass should be in which other MyClass. I have the following code that properly serializes the list of MyClass into a list of string. However, it doesn't deserialize the list of string.
//List of actual object. It's what I use when I work with the object.
[XmlIgnore]
public List<TaskConfiguration> ChildTasks { get; set; }
//Used by the serializer to get the string list, and used
//by the serializer to deserialize the string list to.
[XmlArray("ChildTasks")]
public List<string> ChildTasksSurrogate
{
get
{
List<string> childTaskList = new List<string>();
if (ChildTasks != null)
childTaskList.AddRange(ChildTasks.Select(ct => ct.Name).ToList());
if (_childTasksSurrogate != null)
childTaskList.AddRange(_childTasksSurrogate);
//Clears it not to use it when it serializes.
_childTasksSurrogate = null;
return childTaskList;
}
set
{
_childTasksSurrogate = value;
}
}
[XmlIgnore]
private List<string> _childTasksSurrogate;
As I said, the serialization works. The problem lies with the deserialization. After the deserialization, MyClass._childTasksSurrogate is null.
The problem was related to HOW does the XmlSerializer deserializes the Xml :
I thought that the XmlSerializer would assign the whole property (read: myList = DeserializedList), while it looks like it adds all the elements (read: myList.AddRange(DeserializedList).
I've got a class which has been serialized into JSON, and which I'm trying to deserialize into an object.
e.g.
public class ContentItemViewModel
{
public string CssClass { get; set; }
public MyCustomClass PropertyB { get; set; }
}
the simple property (CssClass) will deserialize with:
var contentItemViewModels = ser.Deserialize<ContentItemViewModel>(contentItems);
But PropertyB gets an error...
We added a JavaScriptConverter:
ser.RegisterConverters(new List<JavaScriptConverter>{ publishedStatusResolver});
But when we added 'MyCustomClass' as a 'SupportedType', the Deserialize method was never called. However when we have ContentItemViewModel as the SupportedType, then Deserialize is called.
We've got a current solution which looks something like this:
class ContentItemViewModelConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var cssClass = GetString(dictionary, "cssClass"); //I'm ommitting the GetString method in this example...
var propertyB= GetString(dictionary, "propertyB");
return new ContentItemViewModel{ CssClass = cssClass ,
PropertyB = new MyCustomClass(propertyB)}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new Exception("Only does the Deserialize");
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new List<Type>
{
typeof(ContentItemViewModel)
};
}
}
}
But we'd prefer a simpler solution of only deserializing MyCustomClass, as there are a number of other fields which are on the ViewModel, and it seems a waste to have to edit this converter every time we change/add a property....
Is there a way to Deserialize JUST PropertyB of type MyCustomClass?
Thanks for your help!
Have you considered using DatacontractJsonSerializer
[DataContract]
public class MyCustomClass
{
[DataMember]
public string foobar { get; set; }
}
[DataContract]
public class ContentItemViewModel
{
[DataMember]
public string CssClass { get; set; }
[DataMember]
public MyCustomClass PropertyB { get; set; }
}
class Program
{
static void Main(string[] args)
{
ContentItemViewModel model = new ContentItemViewModel();
model.CssClass = "StackOver";
model.PropertyB = new MyCustomClass();
model.PropertyB.foobar = "Flow";
//Create a stream to serialize the object to.
MemoryStream ms = new MemoryStream();
// Serializer the User object to the stream.
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContentItemViewModel));
ser.WriteObject(ms, model);
byte[] json = ms.ToArray();
ms.Close();
string s= Encoding.UTF8.GetString(json, 0, json.Length);
Console.ReadLine();
}
}
Add all possible classes to DatacontractJsonSerializer.KnownTypes if MyCustomClass has derivations.
For whatever it may be worth after all this time, but I stumbled over the same problem and the solution is that the Deserializer hasn't got a clue about the classes you are deserializing unless you give him the necessary information.
On the top level, it knows the type from the type parameter of Deserialize<>(). That's why your converter for ContentItemViewModel works. For nested objects, it needs __type properties and a JavaScriptTypeResolver.
var ser = new JavaScriptSerializer(new SimpleTypeResolver());
ser.RegisterConverters(myconverters);
MyClass myObject = new MyClass();
string json = ser.Serialize(myObject);
// set a breakpoint here to see what has happened
ser.Deserialize<MyClass>(json);
A TypeResolver adds a __type property to each serialized object. You can write a custom type resolver that uses short names. In this sample, I use the SimpleTypeResolver from .net that "simply" stores the fully qualified type name as __type. When deserializing, the JavaScriptDeserializer finds __type and asks the TypeResolver for the correct type. Then it knows a type and can call a registered JavaScriptConverter.Deserialize method.
Without a TypeResolver, objects are deserialized to a Dictionary because JavaScriptSerializer doesn't have any type information.
If you can't provide a __type property in your json string, I think you'll need to deserialize to Dictionary first and then add a "guessing-step" that interprets the fields to find the right type. Then, you can use the ConvertToType method of JavaScriptSerializer to copy the dictionary into the object's fields and properties.
If you need to use the JavaScriptSerializer that is provides by ASP.NET and can't create your own, consider this section from the .ctor help of JavaScriptSerializer:
The instance of JavaScriptSerializer that is used by the asynchronous communication layer for invoking Web services from client script uses a special type resolver. This type resolver restricts the types that can be deserialized to those defined in the Web service’s method signature, or the ones that have the GenerateScriptTypeAttribute applied. You cannot modify this built-in type resolver programmatically.
Perhaps the GenerateScriptType Attribute can help you. But I don't know what kind of __type Properties are be needed here.