Serializing cyclic object references using DataContractSerializer not working - c#

I'm building an XNA game and I'm trying to save game/map etc. state completely, and then be able to load and resume from exactly the same state.
My game logic consists of fairly complex elements (for serializing) such as references, delegates etc. I've done hours of research and decided that it's the best to use a DataContractSerializer that preserves the object references. (I also got around for delegates but that's another topic) I have no problem serializing and deserializing the state, re-creating the objects, the fields, lists, and even object references correctly and completely. But I've got a problem with cyclic references. Consider this scenario:
class X{
public X another;
}
//from code:
X first = new X();
X second = new X();
first.another = second;
second.another = first;
Trying to serialize X will result in an exception complaining about cyclic references. If I comment out the last line it works fine. Well, I can imagine WHY it is happening, but I have no idea HOW to solve it. I've read somewhere that I can use the DataContract attribute with IsReference set to true, but it didn't change anything for me -- still got the error. (I want to avoid it anyway since the code I'm working on is portable code and may someday run on Xbox too, and portable library for Xbox doesn't support the assembly that DataContract is in.)
Here is the code to serialize:
class DataContractContentWriterBase<T> where T : GameObject
{
internal void Write(Stream output, T objectToWrite, Type[] extraTypes = null)
{
if (extraTypes == null) { extraTypes = new Type[0]; }
DataContractSerializer serializer = new DataContractSerializer(typeof(T), extraTypes, int.MaxValue, false, true, null);
serializer.WriteObject(output, objectToWrite);
}
}
and I'm calling this code from this class:
[ContentTypeWriter]
public class PlatformObjectTemplateWriter : ContentTypeWriter<TWrite>
(... lots of code ...)
DataContractContentWriterBase<TWrite> writer = new DataContractContentWriterBase<TWrite>();
protected override void Write(ContentWriter output, TWrite value)
{
writer.Write(output.BaseStream, value, GetExtraTypes());
}
and for deserialization:
class DataContractContentReaderBase<T> where T: GameObject
{
internal T Read(Stream input, Type[] extraTypes = null)
{
if (extraTypes == null) { extraTypes = new Type[0]; }
DataContractSerializer serializer = new DataContractSerializer(typeof(T), extraTypes, int.MaxValue, false, true, null);
T obj = serializer.ReadObject(input) as T;
//return obj.Clone() as T; //clone falan.. bi bak iste.
return obj;
}
}
and it's being called by:
public class PlatformObjectTemplateReader : ContentTypeReader<TRead>
(lots of code...)
DataContractContentReaderBase<TRead> reader = new DataContractContentReaderBase<TRead>();
protected override TRead Read(ContentReader input, TRead existingInstance)
{
return reader.Read(input.BaseStream, GetExtraTypes());
}
where:
PlatformObjectTemplate was my type to write.
Any suggestions?
SOLUTION: Just a few minutes ago, I've realized that I wasn't marking the fields with DataMember attribute, and before I added the DataContract attribute, the XNA serializer was somehow acting as the "default" serializer. Now, I've marked all the objects, and things are working perfectly now. I now have cyclic references with no problem in my model.

If you don't want to use [DataContract(IsReference=true)] then DataContractSerializer won't help you, because this attribute is the thing that does the trick with references.
So, you should either look for alternative serializers, or write some serialization code that transforms your graphs into some conventional representation (like a list of nodes + a list of links between them) and back, and then serialize that simple structure.
In case you decide to use DataContract(IsReference=true), here's a sample that serializes your graph:
[DataContract(IsReference = true)]
class X{
[DataMember]
public X another;
}
static void Main()
{
//from code:
var first = new X();
var second = new X();
first.another = second;
second.another = first;
byte[] data;
using (var stream = new MemoryStream())
{
var serializer = new DataContractSerializer(typeof(X));
serializer.WriteObject(stream, first);
data = stream.ToArray();
}
var str = Encoding.UTF8.GetString(data2);
}
The str will contain the following XML:
<X z:Id="i1" xmlns="http://schemas.datacontract.org/2004/07/GraphXmlSerialization"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<another z:Id="i2">
<another z:Ref="i1"/>
</another>
</X>

Related

Validate object against a schema before serialization

I want to serialize a C# object as JSON into a stream, but to avoid the serialization if the object is not valid according to a schema. How should I proceed with this task using JSON.NET and Json.NET Schema? From what I see there is no method in the JSON.NET library which allows the validation of a C# object against a JSON schema. It seems somewhat weird that there is no direct method to just validate the C# object without encoding it. Do you have any idea why this method is not available?
It seems this API not currently available. At a guess, this might be because recursively generating the JSON values to validate involves most of the work of serializing the object. Or it could just be because no one at Newtonsoft ever designed, specified, implemented, tested, documented and shipped that feature.
If you want, you could file an enhancement request requesting this API, probably as a part of the SchemaExtensions class.
In the meantime, if you do need to test-validate a POCO without generating a complete serialization of it (because e.g. the result would be very large), you could grab NullJsonWriter from Reference to automatically created objects, wrap it in a JSchemaValidatingWriter and test-serialize your object as shown in Validate JSON with JSchemaValidatingWriter. NullJsonWriter doesn't actually write anything, and so using it eliminates the performance and memory overhead of generating a complete serialization (either as a string or as a JToken).
First, add the following static method:
public static class JsonExtensions
{
public static bool TestValidate<T>(T obj, JSchema schema, SchemaValidationEventHandler handler = null, JsonSerializerSettings settings = null)
{
using (var writer = new NullJsonWriter())
using (var validatingWriter = new JSchemaValidatingWriter(writer) { Schema = schema })
{
int count = 0;
if (handler != null)
validatingWriter.ValidationEventHandler += handler;
validatingWriter.ValidationEventHandler += (o, a) => count++;
JsonSerializer.CreateDefault(settings).Serialize(validatingWriter, obj);
return count == 0;
}
}
}
// Used to enable Json.NET to traverse an object hierarchy without actually writing any data.
class NullJsonWriter : JsonWriter
{
public NullJsonWriter()
: base()
{
}
public override void Flush()
{
// Do nothing.
}
}
Then use it like:
// Example adapted from
// https://www.newtonsoft.com/jsonschema/help/html/JsonValidatingWriterAndSerializer.htm
// by James Newton-King
string schemaJson = #"{
'description': 'A person',
'type': 'object',
'properties': {
'name': {'type':'string'},
'hobbies': {
'type': 'array',
'maxItems': 3,
'items': {'type':'string'}
}
}
}";
var schema = JSchema.Parse(schemaJson);
var person = new
{
Name = "James",
Hobbies = new [] { ".Net", "Blogging", "Reading", "XBox", "LOLCATS" },
};
var settings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
var isValid = JsonExtensions.TestValidate(person, schema, (o, a) => Console.WriteLine(a.Message), settings);
// Prints Array item count 5 exceeds maximum count of 3. Path 'hobbies'.
Console.WriteLine("isValid = {0}", isValid);
// Prints isValid = False
Watch out for cases by the way. Json.NET schema is case sensitive so you will need to use an appropriate contract resolver when test-validating.
Sample fiddle.
You cannot do that from the JSON string, you need an object and a schema to compare with first..
public void Validate()
{
//...
JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}");
JToken stringToken = JToken.FromObject("pie");
stringToken.Validate(schema);

Automate a copy constructor in C#

I'm trying to find the best way to make copies of a pretty big class. It has somewhere around 80 properties. I could of course code them all in a normal copy constructor, but I'm not sure how nice that would look in code.
So I'm thinking... Is there a way to iterate through the properties of obj A and assign the the values to the corresponding properties of obj B?
This queston is marked as a duplicate, but it is not. My question is not how to make a deep copy, the question is how to iterate through the properties and thus make a normal copy constructor with many properties.
Here is one way:
public static T DeepClone<T>(T original)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "original");
}
if (ReferenceEquals(original, null))
{
return default(T);
}
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter
{
Context = new StreamingContext(StreamingContextStates.Clone)
};
formatter.Serialize(stream, original);
stream.Position = 0;
return (T) formatter.Deserialize(stream);
}
}
This is adapted from CLR via C# by Jeffrey Richter.
You use it like this:
var objB = DeepClone(objA);
The type must be serializable for this to work, though.

Cloning dynamic object in c#

I have a problem cloning dynamic object with the code like this:
public void Execute(IPrepareData entity)
{
try
{
dynamic data = entity.Primary as dynamic;
data.PreviousInfo = deepClone(data.Info);
}
catch (Exception ex)
{
data.Errors.Add(ex.Message);
}
}
private static T deepClone<T>(T obj)
{
if (typeof(T).IsClass || typeof(T).IsArray)
{
if (ReferenceEquals(obj, null))
{
return default(T);
}
}
using (var memoryStream = new MemoryStream())
{
BinaryFormatter fieldFormatter = new BinaryFormatter();
fieldFormatter.Serialize(memoryStream, obj);
memoryStream.Position = 0;
return (T)fieldFormatter.Deserialize(memoryStream);
}
}
dynamic data;
I don't know the structure of entity in advance (only that it will contain Info, and I don't know the structure of info) and that it won't be marked serializable. I need to copy this info to previous info section of entity.
Result of execution of this code is 'Object reference not set to an instance of an object' on fieldFormatter.Serialize line.
How can I check if it is an instance of an object?
There might be (most probably will be) circular references, so I am not trying reflection as I am not sure how to deal with that. Also speed is not an issue.
What about
var clone = JsonConvert.DeserializeObject<dynamic>(JsonConvert.SerializeObject(obj));
If you don't know that the data will be marked serializable, then you can't rely on using BinaryFormatter.
If the object is likely to have circular references, a lot of other serializers are out of the question.
If we assume it is the general case of dynamic (and not just ExpandoObject), then there is no way of getting information about the members, since they can be invented as they are queried.
Basically, this scenario *has no good answer. There is no magic way to just deep clone "a thing".
I have been using JSON.net for serializing user defined types and it has been working well.
There are flags to ignore null properties, or it will by default save as
{propname: 'undefined'}
I know you mentioned speed as not being an issue, but the serializer is very fast.
Here is the nuget package.

Implementing IFormatter recursively

I'm trying to implement a custom formatter using the .NET IFormatter interface.
After a couple of hours of search, I just found a very basic sample which unfortunately doesn't include recursion. I also tried with Reflector to look at BinaryFormatter and SoapFormatter, but they are rather complex.
My question is:
Should I implement recursion myself, or there's something I've missed in FormatterServices?
Following my code:
public void Serialize(Stream serializationStream, object graph)
{
// Get fields that are to be serialized.
MemberInfo[] members = FormatterServices.GetSerializableMembers(graph.GetType(), Context);
// Get fields data.
object[] data = FormatterServices.GetObjectData(graph, members);
// Write class name and all fields & values to file
StreamWriter sw = new StreamWriter(serializationStream);
string accumulator = string.Empty;
for (int i = 0; i < data.Length; ++i)
{
// Skip this field if it is marked NonSerialized.
if (Attribute.IsDefined(members[i], typeof(NonSerializedAttribute)))
continue;
FieldInfo field = (FieldInfo)members[i];
if (field.FieldType.IsPrimitive)
{
}
else //TODO: What should I do here?
}
sw.Close();
}
If by recursion you mean traversing through the object tree then yes, it up to you when you implement your own IFormatter.
Simply check if the value of the property is not null and if it is implementing IFormatter interface. If it is then just call it and use the value it returns.
If not then it is up to you again: you may throw an exception saying that IFormatter must be implemented, or just fall-back to some sort of default formatter (XML or Binary one).
The recursion per se is tricky. When, let's say, the object references itself, you need to be smart enough to handle this situation and not to end up with the infinite loop:
public class A {
public object SomeProperty { get; set; }
}
var a = new A();
a.SomeProperty = a;
There are a number of tricky aspects in implementing formatters, like what if two properties are actually reference the same object? Will you serialize/format it twice or just once and keep the information about these references somehow?
You don't probably need this if you want just one-way serialization, but if you want to be able to restore the object it might be important...

C# memcpy equivalent

I have 2 objects from the same type and i would like to shallow copy one state to the other. In C++ i have memcpy which is great. How can i do it in C#? The MemberwiseClone() is not good enough because it creates & returns a new object and i like to copy to an existing object. I thought of using reflection but i'm afraid it will be too slow for production code. I also thought of using one of the .Net serializers but i think they also create object rather than setting an existing one.
My Use Case:
I have a template object (class not struct) which needs to be updated by one of its instances (objects made of this template)
Any ideas?
In C# (and in C++ too), there is no difference between "new object" and "a copy of existing object" as long as all their members equal to each other.
Given:
Int32 a = 5;
, both operations:
Int32 b = 5;
Int32 b = a;
yield the same result.
As stated in MSDN reference:
The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object.
If a field is a value type, a bit-by-bit copy of the field is performed.
If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
, i.e. it does just the same as memcpy() in C++
[edit] regarding your clarification:
As I understand, you have N objects, each has a (direct) reference to the template object. You want to write back to the template so all objects "see" these changes.
Suggestion: imlement a template broker.
class TemplateProvider
{
public MyData Template { get; set; }
}
Instead of passing the template, pass the template provider to the objects.
to simplyfy the syntax in the components, you can add a (private/internal?) property
MyData Template { get { return m_templateProvider.Template; } }
void UpdateTemplate() { m_templateProvider.Template =
(MyData) this.MemberwiseClone(); }
The template provider also simplifies locking in multithreaded scenarios.
In short, no way unless you do it yourself. But why not create a new object if you override all properties anyway?
memcopy and similar low level constructs are not supported since they undermine guarantees made by the environment.
A shallow copy for structs is made by assignment. For classes, MemberwiseClone is the method to do that - but as you say that creates a new object.
There is no built in way for that, and as it potentially breaks encapsulation it should be used with care anyway.
You could build a generic routine using reflection, but whether it works or not depends on the class itself. And yes, ti will be comparedly slow.
What's left is supporting it by a custom interface. You can provide a generic "Shallow Copy" routine that checks for the interface and uses that, and falls back to reflection when it doesn't. This makes the functionality available generally, and you can optimize the classes for which performance matters later.
I guess you could just do something like:
YourObjectType A = new YourObjectType();
YourObjectType B = a.MemberwiseClone();
This will create a new object inside the MemberwiseClone method an make the B object reference it. I guess it serves your purposes.
Assignment of one struct to another, for all intents and purposes, works exactly like memcpy in C++ on POD objects.
If you feel that this doesn't apply in your situation then I can assure you that your C++ code was not standard-conforming (i.e., contained bugs in the form of undefined behaviour). Please specify (in the question) what effect you want to achieve. This will be more useful than talking about replicating undefined behaviour in another language.
namespace WindowsFormsApplication7
{
[Serializable] // just put this in your class
class Mate
{
public string SomeProperty { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var mA = new Mate();
mA.SomeProperty = "Hey";
var vf = new BinaryFormatter();
var ns = new MemoryStream();
vf.Serialize(ns, mA);
byte[] vytes = ns.ToArray();
var vfx = new BinaryFormatter();
var nsx = new MemoryStream();
nsx.Write(vytes, 0, vytes.Length);
nsx.Seek(0, 0);
var mB = (Mate)vfx.Deserialize(nsx);
mA.SomeProperty = "Yo";
MessageBox.Show(mA.SomeProperty); // Yo
MessageBox.Show(mB.SomeProperty); // Hey
}
}
}
C# / .Net memcpy equivalent is Buffer.MemoryCopy .
void MemoryCopy (void* source, void* destination, long destinationSizeInBytes, long sourceBytesToCopy);
https://learn.microsoft.com/en-us/dotnet/api/system.buffer.memorycopy?view=net-5.0
namespace WindowsFormsApplication7
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var dt = new DataTable();
dt.Columns.Add("lastname", typeof(string));
dt.Columns.Add("firstname", typeof(string));
dt.Rows.Add("lennon", "john");
dt.Rows.Add("mccartney", "paul");
var ms = new MemoryStream();
var bf = new BinaryFormatter();
bf.Serialize(ms, dt);
byte[] bytes = ms.ToArray();
var bfx = new BinaryFormatter();
var msx = new MemoryStream();
msx.Write(bytes, 0, bytes.Length);
msx.Seek(0, 0);
// doesn't just copy reference, copy all contents
var dtx = (DataTable)bfx.Deserialize(msx);
dtx.Rows[0]["lastname"] = "Ono";
// just copy reference
var dty = dt;
dty.Rows[0]["lastname"] = "Winston";
MessageBox.Show(dt.Rows[0]["lastname"].ToString()); // Winston
MessageBox.Show(dtx.Rows[0]["lastname"].ToString()); // Ono
MessageBox.Show(dty.Rows[0]["lastname"].ToString()); // Winston
}
}
}

Categories