Setting an additional caller-defined context for DataContractJsonSerializer - c#

For BinaryFormatter I can set an additional caller-defined context, which will be passed as parameter to the method called after deserialization, e.g. decorated with [OnDeserialized] attribute.
Calling the method decorated with [OnDeserialized] for DataContractJsonSerializer works, but how can I set an additional caller-defined context for it? Is it possible?
I would like to avoid to call some dedicated method like SetBusinessObj(..) manually after deserialization.
var businessObject = "some business object";
var serObj = new SerialClass(businessObject) { Data1 = "some data1" };
using(var ms = new MemoryStream())
{
var bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Other, businessObject));
bf.Serialize(ms, serObj);
ms.Flush();
ms.Position = 0;
var deserObj = bf.Deserialize(ms);
}
using(var ms = new MemoryStream())
{
//How to set an additional caller-defined context?
var jsnSer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(SerialClass));
jsnSer.WriteObject(ms, serObj);
ms.Flush();
ms.Position = 0;
var deserObj = jsnSer.ReadObject(ms);
}
[Serializable]
class SerialClass
{
public string Data1 { get; set; }
private object _businessObj = null;
public SerialClass(object bo)
{
_businessObj = bo;
}
[OnDeserialized]
private void OnDeserialized(StreamingContext sctx)
{
if(sctx.Context is string dataIsHere)
{
_businessObj = dataIsHere;
}
}
}

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.

Turn BsonArray into List<T>

I've been working at this and have managed to get the json parsed into a C# object. Now the next step was to parse a json array into a List. I have managed to get the job done but I'm pretty sure there is a better way to convert from BsonArray to a List
using (StreamReader file = File.OpenText(filename))
{
try
{
//first up convert to bson
var jsonSampleData = file.ReadToEnd();
//var bsonSampleData = BsonDocument.Parse(jsonSampleData);
//this would be for a single BSOnDocument
var bsonSampleData = BsonSerializer.Deserialize<BsonArray>(jsonSampleData);
var x = bsonSampleData.ToList();
List<ThePlan> lst = new List<ThePlan>();
foreach (var doc in x)
{
var t = BsonSerializer.Deserialize<ThePlan>(doc.AsBsonDocument);
lst.Add(t);
}
}
catch (Exception ex)
{
throw;
}
Edit-Additional Information
To be clear what I am needing to accomplish is taking the given json document and rehydrate it to List. This is further complicated by my being new to mongo and T is a mongo entity representation.
As Andrei pointed out it works fine:
using (StreamReader file = File.OpenText(filename))
{
var jsonSampleData = file.ReadToEnd();
_thePlan = BsonSerializer.Deserialize<List<ThePlan>>(jsonSampleData);
}
Thinking about my struggles yesterday I think it actually had to do with my json where on my early attempts it looked like this:
{
"_id": "57509afbc6b48d3f33b2dfcd",
...
}
In the process of figuring it all out my json matured to:
{
"_id": { "$oid": "57509afbc6b48d3f33b2dfcd" },
.....
}
The troubles I was having with BsonSerializer was likely my bad json and once that was worked out I wasn't astute enough to go back to the BsonSerielizer and try again.
Either go strongly typed all the way or not typed at all.
strongly typed
Assuming these are your types:
public class BaseObject {
[BsonId] public ObjectId id { get; set; }
[BsonElement("plans")] public List<ThePlan> Plans { get; set; }
}
public class ThePlan {
[BsonElement("i")] public int Integer { get; set; }
[BsonElement("s")] public string String { get; set; }
}
and these test utilities:
void ToJsonTyped(BaseObject bo)
{
var sb = new StringBuilder();
using (TextWriter tw = new StringWriter(sb))
using (BsonWriter bw = new JsonWriter(tw))
{
BsonSerializer.Serialize<BaseObject>(bw, bo);
}
string jsonObject = sb.ToString();
BaseObject bo2 = BsonSerializer.Deserialize<BaseObject>(jsonObject);
Assert.AreEqual(bo, bo2);
}
void ToBsonTyped(BaseObject bo)
{
byte[] bsonObject = null;
using (var ms = new MemoryStream())
using (BsonWriter bw = new BsonBinaryWriter(ms))
{
BsonSerializer.Serialize<BaseObject>(bw, bo);
bsonObject = ms.ToArray();
}
BaseObject bo1 = BsonSerializer.Deserialize<BaseObject>(bsonObject);
Assert.AreEqual (bo, bo1);
}
you can test:
BaseObject bo = new BaseObject() {
Plans = new List<ThePlan>() {
new ThePlan() {Integer=1, String="one" },
new ThePlan() {Integer=2, String="two" },
new ThePlan() {Integer=3, String="three" } } };
ToBsonTyped(bo);
ToJsonTyped(bo);
not typed at all, combo of BsonDocument and BsonArray
test:
BsonDocument doc = new BsonDocument();
var bsonArray = new BsonArray();
bsonArray.Add(new BsonDocument("one", 1));
bsonArray.Add(new BsonDocument("two", 2));
bsonArray.Add(new BsonDocument("three", 3));
doc.Add( new BsonElement("plans", bsonArray));
ToBsonUnTyped(doc);
ToJsonUnTyped(doc);
test utils:
void ToBsonUnTyped(BsonDocument doc) {
byte[] bsonObject = null;
using (var ms = new MemoryStream())
using (BsonWriter bw = new BsonBinaryWriter(ms))
{
BsonSerializer.Serialize<BsonDocument>(bw, doc);
bsonObject = ms.ToArray();
}
BsonDocument docActual = BsonSerializer.Deserialize<BsonDocument>(bsonObject);
Assert.AreEqual (doc, docActual);
}
void ToJsonUnTyped(BsonDocument doc)
{
var sb = new StringBuilder();
using (TextWriter tw = new StringWriter(sb))
using (BsonWriter bw = new JsonWriter(tw))
{
BsonSerializer.Serialize<BsonDocument>(bw, doc);
}
string jsonObject = sb.ToString();
BsonDocument doc2 = BsonSerializer.Deserialize<BsonDocument>(jsonObject);
Assert.AreEqual(doc, doc2);
}

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.

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