C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
69: XML Serialization Basics
I've seen this happen a dozen times with junior devs: they create a complex class, run it through the XmlSerializer, and then spend two hours wondering why half their data is missing from the resulting file. They assume that if a piece of data exists in the object's memory, the serializer will find a way to put it in the XML.
The Myth: "If it's in the class, it's in the XML"
It's a natural assumption. You have a class, you tell C# to serialize it, and you expect a mirror image of that object in text form. But XmlSerializer is surprisingly picky. It doesn't peek into your private state; it only cares about the public "surface" of your class.
public class PlayerProfile
{
// This will be serialized
public string Username { get; set; }
// This will be COMPLETELY IGNORED
private int _internalId;
public PlayerProfile(string name, int id)
{
Username = name;
_internalId = id;
}
}
If you try to serialize the class above, _internalId simply vanishes. Even worse, the code above will actually throw a InvalidOperationException at runtime. Why? Because XmlSerializer requires a parameterless constructor to recreate the object during deserialization. It doesn't know how to pass "name" or "id" into your custom constructor; it just wants to instantiate the object and then fill the public properties.
The Reality: Public Surface Area and Parameterless Constructors
To make serialization work, you need to ensure your class is "open" enough for the serializer to do its job. This means providing a default constructor (which the compiler does automatically if you don't write any constructors) and ensuring the data you want to save is in public properties.
using System.Xml.Serialization;
using System.IO;
public class PlayerProfile
{
public string Username { get; set; }
public int InternalId { get; set; }
// Required for XmlSerializer
public PlayerProfile() { }
public PlayerProfile(string name, int id)
{
Username = name;
InternalId = id;
}
}
// Usage:
var player = new PlayerProfile("DragonSlayer", 42);
XmlSerializer serializer = new XmlSerializer(typeof(PlayerProfile));
using (StreamWriter writer = new StreamWriter("profile.xml"))
{
serializer.Serialize(writer, player);
}
Now the XML actually contains both the username and the ID. It's a simple trade-off: you sacrifice some encapsulation (making fields public) to gain the ability to easily persist the data.
Refining the Output with Serialization Attributes
By default, XmlSerializer turns every property into an XML element. This results in a very "tall" file. In a real project, you'll often want some data to be attributes of the tag rather than nested elements to keep the file readable and slightly smaller.
I usually use [XmlAttribute] for IDs or metadata and [XmlElement] when I want to explicitly name a tag something different than the C# property name. And if there's a public property that shouldn't be saved—like a cached session token—you use [XmlIgnore].
public class PlayerProfile
{
[XmlAttribute("PlayerID")]
public int InternalId { get; set; }
[XmlElement("DisplayName")]
public string Username { get; set; }
[XmlIgnore]
public string SessionToken { get; set; }
public PlayerProfile() { }
}
With these attributes, your XML changes from a series of generic tags to something more structured: <PlayerProfile PlayerID="42"><DisplayName>DragonSlayer</DisplayName></PlayerProfile>. It's much cleaner and follows standard XML conventions.
📋 Practical Task
Building a Game Settings Persistence System
Your goal is to create a system that saves and loads user preferences for a game. This will test your ability to handle the constructor requirements and use attributes to control the XML output.
- Create a class named
GameSettings. - Add the following properties:
Resolution(string) - This should be serialized as an attribute.Volume(float) - This should be serialized as an element named "MasterVolume".LastLoginTime(DateTime) - This should be ignored during serialization.
- Ensure the class has a parameterless constructor.
- Write a small program that:
- Instantiates
GameSettingswith some values. - Serializes the object to a file named
settings.xml. - Deserializes that file back into a new
GameSettingsobject. - Prints the
ResolutionandVolumeof the loaded object to the console to verify it worked.
- Instantiates
There are no comments for now.