XmlNodeConverter only supports deserializing XmlDocuments error when using Json DeserializeObject - c#

I've the following class;
public class MyClass
{
[XmlIgnore]
public string Name { get; set; }
[XmlElement("Name")]
public XmlCDataSection sName
{
get { return new XmlDocument().CreateCDataSection(Name); }
set { Name = value.Value; }
}
}
I've the following function to take a List<> and copy it's contents;
private static T CloneList<T>(T source)
{
var serialized = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(serialized);
}
But in my code when I try;
List<MyClass> oMyClassList = new List<MyClass>();
MyClass oMyClass = new MyClass();
oMyClass.Name = "Hello World's";
oMyClassList.Add(oMyClass);
List<MyClass> oMyClonedClassList = new List<MyClass>(CloneList(oMyClassList));
At the point of executing the following
List<MyClass> oMyClonedClassList = new List<MyClass>(CloneList(oMyClassList));
I get the error XmlNodeConverter only supports deserializing XmlDocuments. The problem occurs because I've added XmlCDataSection into the class.
How can I get around this problem ?

I managed to overcome this problem by changing my CloneList code to the following
public static T DeepClone<T>(T obj)
{
T objResult;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = (T)bf.Deserialize(ms);
}
return objResult;
}
As provided by [Ajith][1] here How do I clone a generic list in C#?
[1]: https://stackoverflow.com/users/853645/ajith "ajith".
Also, to each of the classes that need to be cloned, I had to add [Serializable] at the top of each class as I was getting the exception 'Not marked as serializable'

Related

A constructor that takes object from same type

So I want to create constructor for my class EmployeeNodeClass that takes In EmployeeNodeClass object and copies it using a deepclone funtion:
public static T DeepClone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
to the new object.
At first i thought that it is as simple as
public EmployeeNodeClass(EmployeeNodeClass EMPND)
{
this = DeepClone(EMPND);
}
but then I got the error that this is readonly.
so how can I do it?
It can be done using NewtonSoft JsonConvert.PopulateObject:
Example:
class Entity
{
public List<int> List { get; set; }
public Entity(List<int> list)
{
List = list;
}
public Entity(Entity original)
{
string originalJson = JsonConvert.SerializeObject(original);
JsonConvert.PopulateObject(originalJson, this);
}
}
And using it:
static void Main(string[] args)
{
var entity1 = new Entity(new List<int> { 1, 2, 3 });
var entity2 = new Entity(entity1);
entity1.List[0] = 5;
Console.WriteLine(entity2.List[0]);
}
Note: since this uses NewtonSoft Json, it will only clone public, writable properties. So internal state in private fields (that are not associated with such properties) will be lost.

Serializing anonymous types

I'd like to convert anonymous type variable to byte[], how can I do that?
What I tried:
byte[] result;
var my = new
{
Test = "a1",
Value = 0
};
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, my); //-- ERROR
result = ms.ToArray();
}
I've got error:
An exception of type
'System.Runtime.Serialization.SerializationException' occurred in
mscorlib.dll but was not handled in user code
Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'
in Assembly 'MyProject, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' is not marked as serializable.Additional
information: Type '<>f__AnonymousType10`2[[System.String, mscorlib,
Can somebody help me? What I'm doing wrong? Or this is not possible to do?
Just create a serializable class
[Serializable]
class myClass
{
public string Test { get; set; }
public int Value { get; set; }
}
And you can serialize your object the following way:
byte[] result;
myClass my = new myClass()
{
Test = "a1",
Value = 0
};
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, my); //NO MORE ERROR
result = ms.ToArray();
}
But i'ts not possible to serialize a anonymous type
Types that are to be serialized with the default serializer family (XmlSerializer, BinaryFormatter, DataContractSerializer, ...) need to be marked [Serializable], need to be public types, and will require public read-write properties.
Anonymous types don't fulfill this role, because they have none of the required properties.
Just create a type that does, and serialize that instead.
Firstly: the correct approach is, as others have pointed out, to create a proper class for serialization.
However, it is actually possible to serialize an anonymous object using Json.Net. Note that I do not recommend actually doing this in a real project - it's a curiosity only.
This code relies on a sneaky way of accessing the underlying type of an anonymous object by using an exemplar object as a type holder:
using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
public class Program
{
static void Main()
{
var data = serializeAnonymousObject();
deserializeAnonymousObject(data);
}
static byte[] serializeAnonymousObject()
{
// This is in a separate method to demonstrate that you can
// serialize in one place and deserialize in another.
var my = new
{
Test = "a1",
Value = 12345
};
return Serialize(my);
}
static void deserializeAnonymousObject(byte[] data)
{
// This is in a separate method to demonstrate that you can
// serialize in one place and deserialize in another.
var deserialized = new // Used as a type holder
{
Test = "",
Value = 0
};
deserialized = Deserialize(deserialized, data);
Console.WriteLine(deserialized.Test);
Console.WriteLine(deserialized.Value);
}
public static byte[] Serialize(object obj)
{
using (var ms = new MemoryStream())
using (var writer = new BsonWriter(ms))
{
new JsonSerializer().Serialize(writer, obj);
return ms.ToArray();
}
}
public static T Deserialize<T>(T typeHolder, byte[] data)
{
using (var ms = new MemoryStream(data))
using (var reader = new BsonReader(ms))
{
return new JsonSerializer().Deserialize<T>(reader);
}
}
}
You can but only if you are insane. Don't use this. This was more a fun problem.
class Program
{
static void Main(string[] args)
{
var obj1 = new
{
Test = "a1",
SubObject = new
{
Id = 1
},
SubArray = new[] { new { Id = 1 }, new { Id = 2 } },
Value = 0
};
var my = new AnonymousSerializer(obj1);
BinaryFormatter bf = new BinaryFormatter();
byte[] data;
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, my);
ms.Close();
data = ms.ToArray();
}
using (MemoryStream ms = new MemoryStream(data))
{
var a = bf.Deserialize(ms) as AnonymousSerializer;
var obj2 = a.GetValue(obj1);
Console.WriteLine(obj1 == obj2);
}
Console.ReadLine();
}
[Serializable]
public class AnonymousSerializer : ISerializable
{
private object[] properties;
public AnonymousSerializer(object objectToSerializer)
{
Type type = objectToSerializer.GetType();
properties = type.GetProperties().Select(p =>
{
if (p.PropertyType.IsArray && IsAnonymousType(p.PropertyType.GetElementType()))
{
var value = p.GetValue(objectToSerializer) as IEnumerable;
return value.Cast<object>().Select(obj => new AnonymousSerializer(obj)).ToArray() ;
}else if (IsAnonymousType(p.PropertyType))
{
var value = p.GetValue(objectToSerializer);
return new AnonymousSerializer(value);
}else{
return p.GetValue(objectToSerializer);
}
}).ToArray();
}
public AnonymousSerializer(SerializationInfo info, StreamingContext context)
{
properties = info.GetValue("properties", typeof(object[])) as object[];
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("properties", properties);
}
public T GetValue<T>(T prototype)
{
return GetValue(typeof(T));
}
public dynamic GetValue(Type type)
{
Expression<Func<object>> exp = Expression.Lambda<Func<object>>(Creator(type));
return exp.Compile()();
}
private Expression Creator(Type type)
{
List<Expression> param = new List<Expression>();
for (int i = 0; i < type.GetConstructors().First().GetParameters().Length; i++)
{
var cParam = type.GetConstructors().First().GetParameters()[i];
if (cParam.ParameterType.IsArray && IsAnonymousType(cParam.ParameterType.GetElementType()))
{
var items = properties[i] as AnonymousSerializer[];
var itemType = cParam.ParameterType.GetElementType();
var data = items.Select(aser => aser.Creator(itemType)).ToArray();
param.Add(Expression.NewArrayInit(itemType, data));
}
else if (IsAnonymousType(cParam.ParameterType))
{
param.Add((properties[i] as AnonymousSerializer).Creator(cParam.ParameterType));
}
else
{
param.Add(Expression.Constant(properties[i]));
}
}
return Expression.New(type.GetConstructors().First(), param);
}
private static bool IsAnonymousType(Type type)
{
bool hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
bool nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
bool isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;
return isAnonymousType;
}
}
}
}
Similar to previous answers, I am using JSON.NET hack;
public static byte[] DynamicToByteArray(object message)
{
string serializeObject = JsonConvert.SerializeObject(message);
byte[] bytes = Encoding.UTF8.GetBytes(serializeObject);
return bytes;
}
I am using dynamic objects my logging purposes, and it works very well since I do not need schema for that.

How can I convert from an IList<x> to a JSON string and back?

I have the following class:
public class TestQuestionHeader
{
public int Id { get; set; }
public int QId { get; set; }
}
and JSON methods:
public static string ToJSONString(this object obj)
{
using (var stream = new MemoryStream())
{
var ser = new DataContractJsonSerializer(obj.GetType());
ser.WriteObject(stream, obj);
return Encoding.UTF8.GetString(stream.ToArray());
}
}
public static T FromJSONString<T>(this string obj)
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(obj)))
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
T ret = (T)ser.ReadObject(stream);
return ret;
}
}
I am using these as follows:
IList<TestQuestionHeaders> testQuestionHeaders = xxx;
string test.Questions = JSON.ToJSONString(testQuestionHeaders);
var a = JSON.FromJSONString<TestQuestionHeader>(test.Questions);
Can someone help me to explain why the variable a contains TestQuestionHeader
instead of IList<TestQuestionHeader>
Your FromJSONString method returns an instance of type T. Your call in your example passes TestQuestionHeader as T. You just need to supply the correct type for T.
You'll need to change that line to:
var a = JSON.FromJSONString<List<TestQuestionHeader>>(test.Questions);
That's because your passing TestQuestionHeader as generic type parameter. Try following:
IList<TestQuestionHeader> a = JSON.FromJSONString<List<TestQuestionHeader>>(test.Questions);
Use JSON.NET, the best JSON library for .NET.
See: http://james.newtonking.com/json/help/index.html

Serializing a HashSet in a Dictionary

I have a similar Problem to Serializing a HashSet
I have a Class with a Member of type Dictionary<String,HashSet<T>>
When i serialize the Object with BinaryFormatter, and then deserialize it, it comes up empty.
I don't know how, or where to call the workaround posted here.
Any Hints? thanks in advance.
edit:
i tried to convert the hashset to a list, as one of the comments in the other thread suggested.
the object looks like this:
public class THashSet : HashSet<T> , ISerializable
{
public THashSet(SerializationInfo info, StreamingContext context)
{
var list = (List<T>)info.GetValue("hashset", typeof(List<T>));
foreach (T t in list)
this.Add(t);
}
public override void GetObjectData(SerializationInfo info,StreamingContext context)
{
info.AddValue("hashset", this.ToList<T>());
}
when the object containing the THashSet is deserialized (and the constructor is called), the list is recovered correctly, as i see in the debugger.
but after the serializer is done, the object only contains an empty hashset.
Suppose for your T object you haven't override methods GetHashCode and Equals. You need to do it.
UPD: All depends from your object implementation. And object is not so easy as you say. Your sitation, object:
[Serializable]
public class DataClass
{
public DataClass()
{
}
public DataClass(string name, string description)
{
Name = name;
Description = description;
this.Dictionary = new Dictionary<string, HashSet<DataClass>>();
}
public string Name { get; set; }
public string Description { get; set; }
public Dictionary<string, HashSet<DataClass>> Dictionary { get; set; }
}
And serialization/deserialization code:
DataClass dataClass = new DataClass("name", "descr");
dataClass.Dictionary.Add("key1", new HashSet<DataClass>() { new DataClass("sample11", "descr11"), new DataClass("sample12", "descr12") });
dataClass.Dictionary.Add("key2", new HashSet<DataClass>() { new DataClass("sample21", "descr21"), new DataClass("sample22", "descr22") });
dataClass.Dictionary.Add("key3", new HashSet<DataClass>() { new DataClass("sample31", "descr31"), new DataClass("sample32", "descr32") });
byte[] serialized;
var formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, dataClass);
serialized = stream.ToArray();
}
using (MemoryStream streamDeserial = new MemoryStream())
{
streamDeserial.Write(serialized, 0, serialized.Length);
streamDeserial.Seek(0, SeekOrigin.Begin);
var dictDeserial = formatter.Deserialize(streamDeserial) as DataClass;
}
This code works well.

Clone Whole Object Graph

While using this code to serialize an object:
public object Clone()
{
var serializer = new DataContractSerializer(GetType());
using (var ms = new System.IO.MemoryStream())
{
serializer.WriteObject(ms, this);
ms.Position = 0;
return serializer.ReadObject(ms);
}
}
I have noticed that it doesn't copy the relationships.
Is there any way to make this happen?
Simply use the constructor overload that accepts preserveObjectReferences, and set it to true:
using System;
using System.Runtime.Serialization;
static class Program
{
public static T Clone<T>(T obj) where T : class
{
var serializer = new DataContractSerializer(typeof(T), null, int.MaxValue, false, true, null);
using (var ms = new System.IO.MemoryStream())
{
serializer.WriteObject(ms, obj);
ms.Position = 0;
return (T)serializer.ReadObject(ms);
}
}
static void Main()
{
Foo foo = new Foo();
Bar bar = new Bar();
foo.Bar = bar;
bar.Foo = foo; // nice cyclic graph
Foo clone = Clone(foo);
Console.WriteLine(foo != clone); //true - new object
Console.WriteLine(clone.Bar.Foo == clone); // true; copied graph
}
}
[DataContract]
class Foo
{
[DataMember]
public Bar Bar { get; set; }
}
[DataContract]
class Bar
{
[DataMember]
public Foo Foo { get; set; }
}
Either annotate your classes with [DataContract] or add your child types in the constructor of DatacontractSerializer.
var knownTypes = new List<Type> {typeof(Class1), typeof(Class2), ..etc..};
var serializer = new DataContractSerializer(GetType(), knownTypes);
To perform a deep clone you might consider using a binary serializer:
public static object CloneObject(object obj)
{
using (var memStream = new MemoryStream())
{
var binaryFormatter = new BinaryFormatter(
null,
new StreamingContext(StreamingContextStates.Clone));
binaryFormatter.Serialize(memStream, obj);
memStream.Seek(0, SeekOrigin.Begin);
return binaryFormatter.Deserialize(memStream);
}
}
You need a binary serializer to preserve objects identity during the serialization/deserialization step.

Categories

Resources