Keywords: C# Serialization | XML Serialization | JSON Serialization | StringWriter | System.Text.Json
Abstract: This article provides a comprehensive exploration of object serialization to string in C#, focusing on the core principles of using StringWriter instead of StreamWriter for XML serialization. It explains in detail the critical differences between toSerialize.GetType() and typeof(T) in XmlSerializer construction. The article also extends to JSON serialization methods in the System.Text.Json namespace, covering synchronous/asynchronous serialization, formatted output, UTF-8 optimization, and other advanced features. Through complete code examples and performance comparisons, it offers developers comprehensive serialization solutions.
Fundamental XML Serialization Implementation
In C# development, object serialization is a core technology for data persistence and network transmission. The original file serialization method uses StreamWriter to write objects to disk files:
// Save object to disk file
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();
}
Optimized Solution for String Output
When serialization results need to be returned as strings rather than saved to files, using StringWriter instead of StreamWriter is the optimal solution:
public static string SerializeObject<T>(this T toSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
using(StringWriter textWriter = new StringWriter())
{
xmlSerializer.Serialize(textWriter, toSerialize);
return textWriter.ToString();
}
}
Key improvements in this implementation include:
- Using
StringWriterto build strings in memory, avoiding disk I/O operations - Employing
usingstatements to ensure proper resource disposal - Directly returning string results to meet in-memory processing requirements
Deep Principles of Type Handling
In the XmlSerializer constructor, using toSerialize.GetType() instead of typeof(T) has significant technical implications:
// Correct: Supports all derived types of T
XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
// Incorrect: Throws exceptions when passing derived type instances
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
This distinction stems from the runtime behavior of C#'s generic type system. toSerialize.GetType() returns the actual runtime type of the object, properly handling all subclasses in the inheritance hierarchy. typeof(T) is determined at compile time, and when the method receives instances of derived classes, XmlSerializer cannot recognize the actual serialization type, causing runtime exceptions.
Modern JSON Serialization Solutions
With the popularity of Web APIs and microservices architecture, JSON has become a more prevalent serialization format. The System.Text.Json namespace provides high-performance JSON serialization support.
Basic Serialization Implementation
using System.Text.Json;
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
// Synchronous serialization to string
string jsonString = JsonSerializer.Serialize(weatherForecast);
// Explicitly specifying generic type parameters
string jsonString = JsonSerializer.Serialize<WeatherForecast>(weatherForecast);
File Serialization Operations
// Synchronous file writing
string fileName = "WeatherForecast.json";
string jsonString = JsonSerializer.Serialize(weatherForecast);
File.WriteAllText(fileName, jsonString);
// Asynchronous file writing (recommended for I/O-intensive operations)
await using FileStream createStream = File.Create(fileName);
await JsonSerializer.SerializeAsync(createStream, weatherForecast);
Advanced Serialization Features
Formatted Output Configuration
var options = new JsonSerializerOptions { WriteIndented = true };
string jsonString = JsonSerializer.Serialize(weatherForecast, options);
// Output result:
// {
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 25,
// "Summary": "Hot"
// }
UTF-8 Performance Optimization
For performance-sensitive scenarios, direct serialization to UTF-8 byte arrays is 5-10% faster than string methods:
byte[] jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(weatherForecast);
Complex Object Serialization
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public string? SummaryField;
public IList<DateTimeOffset>? DatesAvailable { get; set; }
public Dictionary<string, HighLowTemps>? TemperatureRanges { get; set; }
public string[]? SummaryWords { get; set; }
}
public class HighLowTemps
{
public int High { get; set; }
public int Low { get; set; }
}
Serialization Behavior Control
System.Text.Json provides rich serialization behavior configuration:
- Default serialization of all public properties, with support for ignoring specific properties
- Automatic escaping of non-ASCII characters and HTML-sensitive characters
- Default compressed JSON output with support for beautified formatting
- Detection of circular references and exception throwing
- Support for custom naming strategies and type converters
Technical Selection Recommendations
When choosing serialization solutions, consider the following factors:
- XML Serialization: Suitable for scenarios requiring strict schema validation and namespace support
- JSON Serialization: Ideal for modern development scenarios like Web APIs and mobile applications
- Performance Requirements: Prefer
System.Text.Jsonfor high-frequency serialization scenarios - Compatibility: Consider smooth transition from
Newtonsoft.JsontoSystem.Text.Jsonfor existing system migrations
By deeply understanding the principles and characteristics of different serialization technologies, developers can select optimal solutions based on specific requirements and build efficient and reliable data processing systems.