.Net Xml Serialization - Storing Reference Instead of Object Copy

.net XML Serialization - Storing Reference instead of Object Copy

It is not possible with XmlSerializer. You could achieve this with DataContractSerializer using the PreserveObjectReferences property. You may take a look at this post which explains the details.

Here's a sample code:

public class Person
{
public string Name;
public Person Friend;
}

class Program
{
static void Main(string[] args)
{
Person p1 = new Person();
p1.Name = "John";

Person p2 = new Person();
p2.Name = "Mike";
p1.Friend = p2;
Person[] group = new Person[] { p1, p2 };

var serializer = new DataContractSerializer(group.GetType(), null,
0x7FFF /*maxItemsInObjectGraph*/,
false /*ignoreExtensionDataObject*/,
true /*preserveObjectReferences : this is where the magic happens */,
null /*dataContractSurrogate*/);
serializer.WriteObject(Console.OpenStandardOutput(), group);
}
}

This produces the following XML:

<ArrayOfPerson z:Id="1" z:Size="2" xmlns="http://schemas.datacontract.org/2004/07/ToDelete" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Person z:Id="2">
<Friend z:Id="3">
<Friend i:nil="true"/>
<Name z:Id="4">Mike</Name>
</Friend>
<Name z:Id="5">John</Name>
</Person>
<Person z:Ref="3" i:nil="true"/>
</ArrayOfPerson>

Now set PreserveObjectReferences to false in the constructor and you will get this:

<ArrayOfPerson xmlns="http://schemas.datacontract.org/2004/07/ToDelete" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Person>
<Friend>
<Friend i:nil="true"/>
<Name>Mike</Name>
</Friend>
<Name>John</Name>
</Person>
<Person>
<Friend i:nil="true"/>
<Name>Mike</Name>
</Person>
</ArrayOfPerson>

It is worth mentioning that the XML produced this way is not interoperable and can only be deserialized with a DataContractSerializer (same remark as with the BinaryFormatter).

How to save a reference of an instance of an object in XML

Is there any way I can save and load the Inv contents to and from the player save file by reference so I can have them use the existing instances setup during initial loading?

Here is one way to solve this issue:

1. Add to your Item class int field Id or something of the kind, and assign for each unique item appropriate unique value.

2. During initial loading create a global cache of all the unique items in your game. For this matter I'd suggest using Dictionary<int, Item> where the key would be Id of the item and value would be the item itself.

3. In your Player class you should store only Id's of the items, so during loading you can obtain instances of Item from the global cache mentioned previously.

Furthermore, you might want to read about flyweight pattern, which is pretty much about sharing instances.

.NET XmlSerializer and multiple references to the same object

Oh the pains of serialization :-> ...

There was never a generic solution for this, I guess that's why MS stripped it out of the Silverlight framework.

I never rely on any automatic serialization mechanisms of the .net framework. For my own models and repositories, I usually know or can easily programmatically determine which properties are simple scalar ones (numbers/strings/etc) and which are links to other objects (as well as which are lists of either).

There are basically 2 scenarios:

1: We want to serialize/transfer only the flat information of objects. In that case I transfer only the respective IDs for properties that link to other objects. The receiver can then make subsequent queries to get all other objects they need.

2: We want to transfer as much information as possible, i.e. deeper nested XML with several levels, mostly for some reporting functionality displaying everything directly using merely some CSS on the XML. In that case, it is actually desired that objects that are the same will be resolved multiple times into the XML tree.

Sometimes I need to tweak the first scenario a little bit in order to avoid too many subsequent query calls, but usually I get along very well. I.e. I have built into our code base that we can specify which additional objects we want to resolve when, and/or it's configured somewhere.

Serializing references to other serialized objects

You could add a wordID to your serializable Word and reference this ID it in your Lexicon class

[Serializable]
public class Word
{
public string WordID;
public List<string> similes;
}

Your Word Class..

<Word>
<WordID>1</WordID>
<Similes>
<string>Hello</string>
<string>Hi</string>
</Similes>
</Word>

Lexicon class

public class Lexicon
{
public List<string> wordIDs;
}

Lexicon could be like

<Lexicon>
<WordIDs>
<string>1</string>
<string>2</string>
</WordIDs>
</Lexicon>

Object reference not set to an instance of an object during xml serialization and problem with selection of combobox item upon loading

I suspect that MajorversionresultLabel is null, or MajorversionresultLabel.Content is null. Thus your statement

if (!string.IsNullOrEmpty(MajorversionresultLabel.Content.ToString()))

will throw a NullReferenceException. Try this instead:

if (MajorversionresultLabel != null && MajorversionresultLabel.Content != null && MajorversionLabel.Content.ToString() != string.Empty)

I bet your NullReferenceException will go away.

Non-intrusive XML Serialization techniques?

Using the System.Xml.Serialization Attributes is putting the nuts and bolts outside of your code. You are defining metadata and with the exception of optional parameters, no extra code is required. Implementing IXmlSerializable and doing the serialization by hand is error prone and should be avoided. Why? You are defining your data 3 times.

  1. XML Schema
  2. Class
  3. Serialization code

Using attributes, you can scrub step 3.

XML and C# has an impedance mismatch. Like it or not, at some point, you will need to define the serialization to create the right document model.

Arguably, the classes you are serializing should not be performing any work. They are just a data store. Try abstracting your logic away from serialized objects - it may give you a warmer feeling.

Update

If you really, really hate attributes, try using the adapter pattern to serialize your model. The XML code will be in a separate class or assembly and you can work with your model across storage mediums. You will suffer the consequence of having to update the serialization separately when you update your model.

How to do reference fixup during deserialization in C#?

There is an Attribute for this purpose.

Implement the following method in any object you want to deserialize:

[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {

}

There are some more Attributes in System.Runtime.Serialization which might help you.

EDIT

I have modified your code a bit:

[Serializable]
public class Pony {
public int Id {
get; set;
}
public string Name {
get; set;
}
public Pony BFF {
get; set;
}

public Pony() {
}

[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context) {
Console.WriteLine(this.Id + " " + this.Name + " " + this.BFF?.Name);
}
}

TestMethod:

var rd = new Pony { Id = 1, Name = "Rainbow Dash" };
var fs = new Pony { Id = 2, Name = "Fluttershy", BFF = rd };
rd.BFF = fs;
var ponies = new List<Pony> { rd, fs };

object returnValue;
using (var memoryStream = new MemoryStream()) {
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, ponies);
memoryStream.Position = 0;
returnValue = binaryFormatter.Deserialize(memoryStream);
}
var xx = (List<Pony>)returnValue;

As you can see, i removed the ISerializable Interface, the private contructor and your GetObjectData - Method.

I did that, because i dont think you really need it as you havent stated, that you ahd made your implementation of (De)Serialization.

This post is another source of information

EDIT 2 (Testmethod remains the same):

Approach One (Full deserialization and serialization)
...

    private Pony(SerializationInfo info, StreamingContext context) {

foreach (SerializationEntry entry in info) {
switch (entry.Name) {
case "Id":
this.Id = (int)entry.Value;
break;
case "Name":
this.Name = (string)entry.Value;
break;
case "BFF":
this.BFF = (Pony)entry.Value;
break;
}
}
}

public void GetObjectData(SerializationInfo info, StreamingContext ontext) {
info.AddValue("Id", Id);
info.AddValue("Name", Name);
info.AddValue("BFF", BFF);
}
}

...

Approach 2 (Recursive object - Id only) :

...

private Pony(SerializationInfo info, StreamingContext context) {

foreach (SerializationEntry entry in info) {
switch (entry.Name) {
case "Id":
this.Id = (int)entry.Value;
break;
case "Name":
this.Name = (string)entry.Value;
break;
case "BFF.Id":
var bffId = (int)entry.Value;
this.BFF = GetPonyById(bffId); // You have to implement this
break;
}
}
}

public void GetObjectData(SerializationInfo info, StreamingContext ontext) {
info.AddValue("Id", Id);
info.AddValue("Name", Name);
info.AddValue("BFF.Id", BFF.Id);
}

...

How to store C# object data as XML using XML Serialization

As per the discussion in comments, I'll give you a basic overview of how you can quickly (de)serialize custom classes.

For reference, here's your comment to which I'm replying:

@Flater Thanks for the reply! I'm still unsure how to do serialize my objects. For example, UserAdministration contains a list of Users and has methods for adding/removing Users from the list. Say for example, I add a new user by calling the addUser method of UserAdministration. How does the new User object get added to the XML file?


I always use this helper class because it makes the code so much cleaner. I've copied it from the internet somewhere, potentially StackOverflow. I will credit the author once I know/remember who it is.

public static class SerializerHelper
{
/// <summary>
/// Serializes an object.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="serializableObject"></param>
/// <param name="fileName"></param>
public static void SerializeObject<T>(string filepath, T serializableObject)
{
if (serializableObject == null) { return; }

try
{
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(filepath);
stream.Close();
}
}
catch (Exception ex)
{
//Log exception here
}
}

/// <summary>
/// Deserializes an xml file into an object list
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fileName"></param>
/// <returns></returns>
public static T DeSerializeObject<T>(string filepath)
{
T objectOut = default(T);

if (!System.IO.File.Exists(filepath)) return objectOut;

try
{
string attributeXml = string.Empty;

XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(filepath);
string xmlString = xmlDocument.OuterXml;

using (StringReader read = new StringReader(xmlString))
{
Type outType = typeof(T);

XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read))
{
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}

read.Close();
}
}
catch (Exception ex)
{
//Log exception here
}

return objectOut;
}
}

Before I get to the usage examples, some tips so you know how to approach this:

  • These methods will serialize a single object (of any type) to a given file. This can be a class Foo, a List<Foo>, or a custom made class that contains all the data you want to save MyFooData
  • You cannot add to an existing file. When saving a file, the old file will be overwritten. I have not yet come across a use case where I needed to add to a file and couldn't just read the file's contents, change them, and store the whole object again. But I've only used it for small scale storage.
  • XML serialization uses public properties and a parameterless constructor. So make sure the objects you want to store have both of these. It will not serialize private or protected properties; or public/private/protected class fields.
  • The XML Serializer will automatically also serialize subclasses that are contained within (if they are public properties!). As far as I'm aware, it can go as deep as you want it to go. It will save everything.
  • The XML serializer has a weakness to recursion. I will explain how to avoid recursion in the code examples.
  • XML Serialization cannot (de)serialize Dictionary<T1,T2> or any IEnumerable that uses hashes for storage. However, if you want to store a Dictionary<T1,T2>, you can store it as a List<KeyValuePair<T1,T2>>, which means it will retain your data; just not the internal hash that it uses for quick lookup.

1 - The simplest example

[Serializable]
public class User
{
public string Name { get; set; }
public string Email { get; set; }
}

public static void TestMethod()
{
var myUser = new User() { Name = "John", Email = "John@john.com" };

//Save to file
SerializerHelper.SerializeObject(@"C:\MyDir\MyFile.txt", myUser);

//Read from file
var myUserReloaded = SerializerHelper.DeSerializeObject<User>(@"C:\MyDir\MyFile.txt");
}

Take note of the [Serializable] attribute on the User class. This is usually the only change that you need to make to your classes to make this work.

2 - Avoiding recursion and stack overflows

As mentioned in the tips, this serialization has a weakness to recursion. This can happen when classes refer to eachother in both directions.

[Serializable]
public class User
{
public string Name { get; set; }
public string Email { get; set; }

public Manager Boss {get; set; }
}

[Serializable]
public class Manager
{
public string Name { get; set; }

public User FavoriteEmployee {get; set; }
}

public static void TestMethod()
{
var userJohn = new User() { Name = "John", Email = "John@john.com" };
var managerMark = new Manager() { Name = "Mark" };

managerMark.FavoriteEmployee = userJohn;
userJohn.Boss = managerMark;

//Save to file
SerializerHelper.SerializeObject(@"C:\MyDir\MyFile.txt", userJohn);

//Read from file
var userJohnReloaded = SerializerHelper.DeSerializeObject<User>(@"C:\MyDir\MyFile.txt");
}

You will get a stack overflow exception when saving the file, because the serializer gets stuck in an infinite loop.

The serializer tries to write all of userJohn's properties to the file. When it gets to the Boss property, it notices that it is a serializable object and will begin serializing all of managerMark's properties. When it gets to the FavoriteEmployee property, it notices that it is a serializable object and will begin serializing all of userJohn's properties. When it...

You can prevent this from happening by using the [XmlIgnore] attribute on one of the two properties (or both, if you want to be really secure).

[Serializable]
public class User
{
public string Name { get; set; }
public string Email { get; set; }

public Manager Boss {get; set; }
}

[Serializable]
public class Manager
{
public string Name { get; set; }

[XmlIgnore]
public User FavoriteEmployee {get; set; }
}

The serializer tries to write all of userJohn's properties to the file. When it gets to the Boss property, it notices that it is a serializable object and will begin serializing all of managerMark's properties. When it gets to the FavoriteEmployee property, it notices that this property is marked [XmlIgnore] and it will not attempt to serialize what is contained within.


I hope this answer is what you were looking for.


EDIT I forgot a big caveat.

Let's say we are storing a List<Child> with two Child objects in it. Both Child objects have a Parent property, and it just so happens that both children are referencing the same Parent (same object in memory).

If I store the Parent (including a list of its children) it will serialize our Parent which will contain both Child objects. When deserializing, you will again have a singular Parent object and the two Child objects you started with.

If I store the List<Child> (including their parent), it will serialize the Parent for both Child objects. However, when deserializing, both Parent objects will be deserialized but they will be separate objects in memory.

As you can see, there could be a potential bug here if you are expecting both children to refer to the same Parent (as an object in memory). For this reason, I have a personal rule about how to use the serialization, and where to put the [XmlIgnore] attribute.

Children (IEnumerable<Foo>) get serialized, parents (Foo) do not. This allows me to store an entire tree in one go.

This means that I have to store the parent (and automatically have its children be serialized too), and I can only store the child without its reference to its parent (unless you store a ParentId key in the Child).

By doing it this way, you ensure that you won't create a multitude of Parent objects through deserialization, because every object only gets mentioned once in the XML file.


Edit

I forgot to add the SerializerHelper. to the method calls. Fixed now.



Related Topics



Leave a reply



Submit