Can't cast objects from Dictionary after using json.net deserialization - c#

I have a problem with deserialized Dictionary. I can't cast objects from Dictionary to my type Remiza... I'm using Json.net and what I can see is that objects in Dictionary are JObject not Object and I can't cast them to my type. Here is my serialization/deserialization code:
private static Dictionary<Type, List<Object>> _ekstensje = new Dictionary<Type, List<Object>>();
public static void SerializeDictionary()
{
string json = JsonConvert.SerializeObject(_ekstensje);
System.IO.StreamWriter file = new System.IO.StreamWriter(#"c:\tmp\dictionary.json");
file.WriteLine(json);
file.Close();
}
public static void DeserializeDictionary()
{
string json;
System.IO.StreamReader file = new System.IO.StreamReader(#"c:\tmp\dictionary.json");
json = file.ReadToEnd();
file.Close();
_ekstensje = JsonConvert.DeserializeObject<Dictionary<Type, List<Object>>>(json);//Deserializacja Dictionary
Debug.WriteLine(_ekstensje);
}
public static List<Object> GetEkstensja(Type className)
{
List<Object> list = _ekstensje[className];
return list;
}
Exectution:
ObjectPlus.DeserializeDictionary();
List<Object> list = ObjectPlus.GetEkstensja(typeof(Remiza));
foreach (Object o in list)
{
Remiza r = (Remiza) o;
listaRemiz.Add(r);
}
My problem is when casting to "Remiza" I have that exception:
An exception of type 'System.InvalidCastException' occurred in Osek_MAS_WPF.exe but was not handled in user code. Additional information: Unable to cast object of type 'Newtonsoft.Json.Linq.JObject' to type 'Osek_MAS_WPF.Remiza'.
Thanks for any help!

This should allow you to convert the JObect to your Remiza type.
ObjectPlus.DeserializeDictionary();
List<Object> list = ObjectPlus.GetEkstensja(typeof(Remiza));
foreach (Object o in list)
{
Remiza r = o.ToObject<Remiza>();
listaRemiz.Add(r);
}
I got this from the stackoverflow answer at the link below. If what I put doesn't work take a look at the link and it should help you to get it running.
https://stackoverflow.com/a/10221594/634769

In order to successfully serialize and deserialize polymorphic types with Json.NET, you need to set TypeNameHandling = TypeNameHandling.Auto, like so:
public class ObjectPlus
{
// Replace with whatever file name is appropriate. My computer doesn't have a "c:\tmp" directory.
static string JsonFileName { get { return Path.Combine(Path.GetTempPath(), "dictionary.json"); } }
static JsonSerializerSettings JsonSettings { get { return new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }; } }
private static Dictionary<Type, List<Object>> _ekstensje = new Dictionary<Type, List<Object>>();
public static void SerializeDictionary()
{
var path = JsonFileName;
using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var writer = new StreamWriter(stream))
{
var serializer = JsonSerializer.CreateDefault(JsonSettings);
serializer.Serialize(writer, _ekstensje);
}
}
public static void DeserializeDictionary()
{
var path = JsonFileName;
try
{
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var reader = new StreamReader(stream))
using (var jsonReader = new JsonTextReader(reader))
{
var serializer = JsonSerializer.CreateDefault(JsonSettings);
_ekstensje = serializer.Deserialize<Dictionary<Type, List<Object>>>(jsonReader);
}
}
catch (FileNotFoundException)
{
// File was not created yet, dictionary should be empty.
_ekstensje.Clear();
}
}
public static List<Object> GetEkstensja(Type className)
{
List<Object> list = _ekstensje[className];
return list;
}
public static void AddEkstensja<T>(T obj)
{
List<Object> list;
if (!_ekstensje.TryGetValue(obj.GetType(), out list))
list = _ekstensje[obj.GetType()] = new List<object>();
list.Add(obj);
}
internal static string ShowJsonContents()
{
if (!File.Exists(JsonFileName))
return string.Empty;
return File.ReadAllText(JsonFileName);
}
}
You should now be able to serialize and deserialize your dictionary when it contains an instance of Remiza.
This will work for types that serialize to objects or collections. However, if your dictionary contains types that serialize to JSON primitives -- for instance an enum or a long -- you may need to encapsulate them in a type wrapper along the lines of Deserialize specific enum into system.enum in Json.Net.
(Incidentally, your _ekstensje dictionary isn't thread-safe.)

Related

Generic XML parser for Poco Objects

Currently I try to write a generic XML parser and having trouble to write a generic Parser Class.
My current Parser:
public class XmlFileLoader
{
public T GetDeserializedData<T>(Type targetType, string rootElementName, string filename)
where T: class
{
XmlDocument doc = new XmlDocument();
XmlTextReader reader = new XmlTextReader(GetPath(filename));
reader.Read();
doc.Load(reader);
T result = DatabaseXmlSerializer.DeserializeXmlString<T>(doc.InnerXml, rootElementName, targetType);
return result;
}
}
My Deserializer:
public static class DatabaseXmlSerializer
{
public static T DeserializeXmlString<T>(string XmlString, string RootElementName , Type TargetType)
{
T tempObject = default;
using (MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(XmlString)))
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = RootElementName;
xRoot.IsNullable = true;
XmlSerializer xs = new XmlSerializer(TargetType, xRoot);
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
tempObject = (T)xs.Deserialize(memoryStream);
}
return tempObject;
}
}
My call:
var loader= new XmlFileLoader();
var books = loader.GetDeserializedData<List<MySolution.Book>>(typeof(List<MySolution.Book>), "Bookstore", "Books.xml");
What is my concern?
I have to pass the type twice, but somehow i can't figure out how to just write it with one type.
I want my call to be like this:
var loader= new XmlFileLoader();
var books = loader.GetDeserializedData<List<MySolution.Book>>("Bookstore", "Books.xml");
There are so many errors in your code that I don't know what to do.
It's easier to rewrite the code completely.
public class XmlFileLoader
{
public T GetData<T>(string rootElementName, string filename)
{
var xmlSerializer = new XmlSerializer(typeof(T),
new XmlRootAttribute(rootElementName));
using (var xmlReader = XmlReader.Create(filename))
return (T)xmlSerializer.Deserialize(xmlReader);
}
}
This is the whole code!
Use it:
var xmlFileLoader = new XmlFileLoader();
var someModel = xmlFileLoader.GetData<SomeModel>("root", "filename");
The deserialization code is so simple that you can just throw out this class and just use XmlSerializer directly where you need it.
However, there is a serious problem when using XmlRootAttribute: multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. See documentation: Dynamically Generated Assemblies.
Therefore, it makes sense to cache serializer instances inside our class. Then its presence becomes justified.
public class XmlFileLoader
{
private static readonly Dictionary<(Type, string), XmlSerializer> serializers
= new Dictionary<(Type, string), XmlSerializer>();
public T GetData<T>(string rootElementName, string filename)
{
var key = (typeof(T), rootElementName);
if (!serializers.TryGetValue(key, out XmlSerializer xmlSerializer))
{
xmlSerializer = new XmlSerializer(typeof(T),
new XmlRootAttribute(rootElementName));
serializers.Add(key, xmlSerializer);
}
using (var xmlReader = XmlReader.Create(filename))
return (T)xmlSerializer.Deserialize(xmlReader);
}
}

How to serialize ANY object into a string?

I'm running into an issue where my JSON serializer is failing randomly due to the character < showing up from time to time. I can't nail down where this is coming from and I want to - on exception - reserialize using a different method so I can see a full representation of the offending object. Is there any way to do this?
My current code:
// data is of type 'object'
serialized = JsonConvert.SerializeObject(data, new JsonSerializerSettings() {
Error = delegate(object sender, ErrorEventArgs args) {
// reserialize here and output object so I know what the heck is going on
}
})
There is no foolproof way to serialize any and every possible c# object.
Instead, you have a few ways to attack your problem:
Turn on Json.NET tracing. See Debugging with Serialization Tracing. This should tell you where in your object graph the problem is occurring.
Rather than serializing with JsonConvert.SerializeObject(), if you serialize with JsonSerializer.Serialize() and write to a string using a JsonTextWriter wrapping a StringWriter, you can flush the writer and log the partial serialization. That may give some idea where the problem arises.
You can try serializing using various other serializers, and if any work, log the result.
If one of your object properties is throwing an exception, you might try to force serialization of fields instead. See JSON.Net: Force serialization of all private fields and all fields in sub-classes.
For instance, putting #1, #2 and #3 together gives the following method:
public static class JsonSerializerExtensions
{
public static string SerializeObject(object obj, JsonSerializerSettings settings = null)
{
settings = settings ?? new JsonSerializerSettings();
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
using (var jsonWriter = new JsonTextWriter(writer))
{
var oldError = settings.Error;
var oldTraceWriter = settings.TraceWriter;
var oldFormatting = settings.Formatting;
try
{
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
if (settings.TraceWriter == null)
settings.TraceWriter = new MemoryTraceWriter();
settings.Error = oldError + delegate(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
jsonWriter.Flush();
var logSb = new StringBuilder();
logSb.AppendLine("Serialization error: ");
logSb.Append("Path: ").Append(args.ErrorContext.Path).AppendLine();
logSb.Append("Member: ").Append(args.ErrorContext.Member).AppendLine();
logSb.Append("OriginalObject: ").Append(args.ErrorContext.OriginalObject).AppendLine();
logSb.AppendLine("Error: ").Append(args.ErrorContext.Error).AppendLine();
logSb.AppendLine("Partial serialization results: ").Append(sb).AppendLine();
logSb.AppendLine("TraceWriter contents: ").Append(settings.TraceWriter).AppendLine();
logSb.AppendLine("JavaScriptSerializer serialization: ");
try
{
logSb.AppendLine(new JavaScriptSerializer().Serialize(obj));
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
logSb.AppendLine("XmlSerializer serialization: ");
try
{
logSb.AppendLine(obj.GetXml());
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
logSb.AppendLine("BinaryFormatter serialization: ");
try
{
logSb.AppendLine(BinaryFormatterExtensions.ToBase64String(obj));
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
Debug.WriteLine(logSb);
};
var serializer = JsonSerializer.CreateDefault(settings);
serializer.Serialize(jsonWriter, obj);
}
finally
{
settings.Error = oldError;
settings.TraceWriter = oldTraceWriter;
settings.Formatting = oldFormatting;
}
}
return sb.ToString();
}
}
public static class XmlSerializerExtensions
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
}
public static string GetXml<T>(this T obj)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " };
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj);
return textWriter.ToString();
}
}
}
public static class BinaryFormatterExtensions
{
public static string ToBase64String<T>(T obj)
{
using (var stream = new MemoryStream())
{
new BinaryFormatter().Serialize(stream, obj);
return Convert.ToBase64String(stream.GetBuffer(), 0, checked((int)stream.Length)); // Throw an exception on overflow.
}
}
public static T FromBase64String<T>(string data)
{
return FromBase64String<T>(data, null);
}
public static T FromBase64String<T>(string data, BinaryFormatter formatter)
{
using (var stream = new MemoryStream(Convert.FromBase64String(data)))
{
formatter = (formatter ?? new BinaryFormatter());
var obj = formatter.Deserialize(stream);
if (obj is T)
return (T)obj;
return default(T);
}
}
}
You would likely replace the final Debug.WriteLine() with an appropriate logging method, then replace JsonConvert.SerializeObject(data) with JsonSerializerExtensions.SerializeObject(data) in your applications code.

JSON .Net Unity Deserialize dictionary of anonymous objects

I have a Dictionary that I serialize onto a binary file, and deserialize back again using JSON .net from https://json.codeplex.com/
The dictionary may contain abritrary objects (string, classes, even List). Each class is [System.Serializable]
At serialize time, I add serializer.TypeNameHandling = TypeNameHandling.All; to make sure the deserializer has the type info required to deserialize the dictionary.
I am unable to deserialize it correctly to the identical list of object, I only get JObjects in my container and not the original type. Can anyone help me accomplish this?
Thanks in advance;
Laurent
Update:
To get data in / out I use those two methods:
public static byte[] SerializeToByteArray<T>(T data)
{
byte[] serializedData = new byte[]{};
using(var stream = new MemoryStream())
{
using (BsonWriter writer = new BsonWriter(stream))
{
JsonSerializer serializer = new JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.All;
serializer.Serialize(writer, data);
}
return stream.ToArray();
}
}
public static T DeserializeFromByteArray<T>(byte[] serializedData )
{
using (var stream = new MemoryStream(serializedData))
{
using (BsonReader reader = new BsonReader(stream))
{
JsonSerializer serializer = new JsonSerializer();
return (T)serializer.Deserialize<T>( reader );
}
}
}
[System.Serializable]
public class FavoriteLevel
{
public FavoriteLevel(string ID, int TYPE) { id = ID; type = TYPE;}
public string id;
public int type;
}
Dictionary<string,object> dict = new Dictionary<string,object>(1);
List<FavoriteLevel> levels = new List<FavoriteLevel>(1);
levels.Add (new FavoriteLevel("123",FavoriteType.Favorite) );
dict.Add ( "123", levels );
byte[] data = SerializeToByteArray( dict );
Dictionary<string,object> incomingDict = DeserializeFromByteArray<Dictionary<string,object>>( data );
object listBack = incomingDict["123"];
// ERROR: listBack is a Json object and not a List<FavoriteLevel> object
You need to set serializer.TypeNameHandling = TypeNameHandling.All when deserializing as well as serializing. Otherwise, the "$type" property will be ignored.
Thus:
public static class JsonExtensions
{
public static byte[] SerializeToByteArray<T>(T data, JsonSerializerSettings settings)
{
using (var stream = new MemoryStream())
{
using (var writer = new BsonWriter(stream))
{
JsonSerializer serializer = JsonSerializer.Create(settings);
serializer.Serialize(writer, data);
}
return stream.ToArray();
}
}
public static T DeserializeFromByteArray<T>(byte[] serializedData, JsonSerializerSettings settings)
{
using (var stream = new MemoryStream(serializedData))
{
using (var reader = new BsonReader(stream))
{
JsonSerializer serializer = JsonSerializer.Create(settings);
return (T)serializer.Deserialize<T>(reader);
}
}
}
}
public static class TestClass
{
public static void Test()
{
Dictionary<string, object> dict = new Dictionary<string, object>(1);
List<FavoriteLevel> levels = new List<FavoriteLevel>(1);
levels.Add(new FavoriteLevel("123", 0));
dict.Add("123", levels);
var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.All;
byte[] data = JsonExtensions.SerializeToByteArray(dict, settings);
Dictionary<string, object> incomingDict = JsonExtensions.DeserializeFromByteArray<Dictionary<string, object>>(data, settings);
object listBack = incomingDict["123"];
Debug.Assert(listBack.GetType() == levels.GetType()); // No assert.
}
}

Create a method that deserializes a serialized object.

I would like to create a method that Deserializes a serialized class.
Codes:
Settings newSettings = new Settings(); //Settings is a class.
Settings lastSettings = new Settings();
private void LoadXML(Type type,string filepath)
{
XmlSerializer serializer = new XmlSerializer(type);
FileStream fs = File.Open(filepath,FileMode.Open);
Settings newset = (Settings)serializer.Deserialize(fs); //Deserializes a serializable class file.
newSettings = newset; //Sets these settings as NewSettings
lastSettings = newSettingsXML; //Before form opening, sets information into form.
fs.Close();
}
Now i want to do these with a method. I created also different method for "Person" Class. This method reads person from xml file and sets these person into the form.
private void PersonFromXML(string filepath)
{
XmlSerializer serializer = new XmlSerializer(typeof(BindingList<Person>));
FileStream fs = File.Open(filepath,FileMode.Open);
BindingList<Person> XML_Person = (BindingList<Person>)serializer.Deserialize(fs);
NEW_XML_Person = XML_Person; // These are BindingList<Person>.
fs.Close();
Grid_Person.DataSource = XML_Person;
}
I want to do these Deserializing methods as one different Method. I want to write this method into a dll file.
I tried to do these:
private BindingList<Type> FromXML(BindingList<Type> type,string filepath)
{
XmlSerializer ser = new XmlSerializer(typeof(type));
FileStream fs = File.Open(filepath,FileMode.Open);
BindingList<Type> BL = (type)ser.Deserialize(fs);
fs.Close();
return BL;
}
But it did NOT work. Because i could not set BindingList type as Person.. What should I do ? Thanks.
Seems that you forgot to set your generic type parameter on your method declaration.
Try this:
public static class MySerializationHelper
{
public static class From
{
public TReturn XMLFile<TReturn>(string contents)
{
var serializer = new XmlSerializer(typeof(TReturn));
var fs = File.Open(filePath, FileMode.Open);
var result = (TReturn)serializer.Deserialize(fs);
fs.Close();
return result;
}
}
public static class To
{
public void XMLFile<TType>(TType object, string filePath)
{
// Serialize it here...
}
}
}
Than, you may simply use it, like:
var bindingList = MySerializationHelper.From.XmlFile<IBindingList<Person>>("Persons.xml");
var person = MySerializationHelper.From.XmlFile<Person>("Person_1.xml");
MySerializationHelper.To.XmlFile<IBindingList<Person>>(bindingList, "Persons_copy.xml");
MySerializationHelper.To.XmlFile<Person>(person, "Person_1_Copy.xml");

C# - Deserialize a List<String>

I can serialize a list really easy:
List<String> fieldsToNotCopy =new List<String> {"Iteration Path","Iteration ID"};
fieldsToNotCopy.SerializeObject("FieldsToNotMove.xml");
Now I need a method like this:
List<String> loadedList = new List<String();
loadedList.DeserializeObject("FieldsToNotMove.xml");
Is there such a method? Or am I going to need to create an XML reader and load it in that way?
EDIT: Turns out there is no built in SerialzeObject. I had made one earlier in my project and forgot about it. When I found it I thought it was built in. In case you are curious this is the SerializeObject that I made:
// Save an object out to the disk
public static void SerializeObject<T>(this T toSerialize, String filename)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
TextWriter textWriter = new StreamWriter(filename);
xmlSerializer.Serialize(textWriter, toSerialize);
textWriter.Close();
}
There is no such builtin method as SerializeObject but it's not terribly difficult to code one up.
public void SerializeObject(this List<string> list, string fileName) {
var serializer = new XmlSerializer(typeof(List<string>));
using ( var stream = File.OpenWrite(fileName)) {
serializer.Serialize(stream, list);
}
}
And Deserialize
public void Deserialize(this List<string> list, string fileName) {
var serializer = new XmlSerializer(typeof(List<string>));
using ( var stream = File.OpenRead(fileName) ){
var other = (List<string>)(serializer.Deserialize(stream));
list.Clear();
list.AddRange(other);
}
}
These are my serialize/deserialize extension methods that work quite well
public static class SerializationExtensions
{
public static XElement Serialize(this object source)
{
try
{
var serializer = XmlSerializerFactory.GetSerializerFor(source.GetType());
var xdoc = new XDocument();
using (var writer = xdoc.CreateWriter())
{
serializer.Serialize(writer, source, new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") }));
}
return (xdoc.Document != null) ? xdoc.Document.Root : new XElement("Error", "Document Missing");
}
catch (Exception x)
{
return new XElement("Error", x.ToString());
}
}
public static T Deserialize<T>(this XElement source) where T : class
{
try
{
var serializer = XmlSerializerFactory.GetSerializerFor(typeof(T));
return (T)serializer.Deserialize(source.CreateReader());
}
catch //(Exception x)
{
return null;
}
}
}
public static class XmlSerializerFactory
{
private static Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>();
public static XmlSerializer GetSerializerFor(Type typeOfT)
{
if (!serializers.ContainsKey(typeOfT))
{
System.Diagnostics.Debug.WriteLine(string.Format("XmlSerializerFactory.GetSerializerFor(typeof({0}));", typeOfT));
var newSerializer = new XmlSerializer(typeOfT);
serializers.Add(typeOfT, newSerializer);
}
return serializers[typeOfT];
}
}
You just need to define a type for your list and use it instead
public class StringList : List<String> { }
Oh, and you don't NEED the XmlSerializerFactory, it's just there since creating a serializer is slow, and if you use the same one over and over this speeds up your app.
I'm not sure whether this will help you but I have dome something which I believe to be similar to you.
//A list that holds my data
private List<Location> locationCollection = new List<Location>();
public bool Load()
{
//For debug purposes
Console.WriteLine("Loading Data");
XmlSerializer serializer = new XmlSerializer(typeof(List<Location>));
FileStream fs = new FileStream("CurrencyData.xml", FileMode.Open);
locationCollection = (List<Location>)serializer.Deserialize(fs);
fs.Close();
Console.WriteLine("Data Loaded");
return true;
}
This allows me to deserialise all my data back into a List<> but i'd advise putting it in a try - catch block for safety. In fact just looking at this now is going to make me rewrite this in a "using" block too.
I hope this helps.
EDIT:
Apologies, just noticed you're trying to do it a different way but i'll leave my answer there anyway.
I was getting error while deserializing to object. The error was "There is an error in XML document (0, 0)". I have modified the Deserialize function originally written by #JaredPar to resolve this error. It may be useful to someone:
public static void Deserialize(this List<string> list, string fileName)
{
XmlRootAttribute xmlRoot = new XmlRootAttribute();
xmlRoot.ElementName = "YourRootElementName";
xmlRoot.IsNullable = true;
var serializer = new XmlSerializer(typeof(List<string>), xmlRoot);
using (var stream = File.OpenRead(fileName))
{
var other = (List<string>)(serializer.Deserialize(stream));
list.Clear();
list.AddRange(other);
}
}
Create a list of products be serialized
List<string> Products = new List<string>
{
new string("Product 1"),
new string("Product 2"),
new string("Product 3"),
new string("Product 4")
};
Serialization
using (FileStream fs = new FileStream(#"C:\products.txt", FileMode.Create))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, Products);
}
Deserialization
using (FileStream fs = new FileStream(#"C:\products.txt", FileMode.Open))
{
BinaryFormatter bf = new BinaryFormatter();
var productList = (List<string>)bf.Deserialize(fs);
}

Categories

Resources