Favicon

XmlSerializer

Peponi1/20/20256m

C#
SystemXmlSerializationDeserialization

1. Introduction

.NET의 XmlSerializer 클래스는 객체 직렬화 및 역직렬화 기능을 제공한다. XmlSerializer를 통해 직렬화 하는 경우, 파일에 쓰거나 스트림으로 내보내는 등의 작업이 가능하다. 기본적으로 직렬화 및 역직렬화의 대상은 객체의 public 멤버 (필드 또는 속성) 이며, 각 멤버는 하나의 element로 표현된다.

여기서는 간단한 사용 방법을 알아보도록 한다.

2. Serialize

여기서는 class, struct, List<T> 형식의 객체를 직렬화하는 예시를 보여준다.

Class
using System.Xml.Serialization;
 
public class TestClass
{
    public int ID { get; set; }
    public string? Name { get; set; }
    public required object Value { get; set; }
 
    public override string ToString() => $"ID: {ID}, Name: {Name}, Value: {Value}";
}
 
public partial class Program
{
    public static void SerializeClass()
    {
        TestClass classData = new() { ID = 1, Name = "class", Value = 10 };
        XmlSerializer serializer = new(typeof(TestClass));
 
        serializer.Serialize(Console.Out, classData);
    }
}
<?xml version="1.0" encoding="Codepage - 949"?>
<TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>1</ID>
  <Name>class</Name>
  <Value xsi:type="xsd:int">10</Value>
</TestClass>
Struct
public struct TestStruct
{
    public int ID { get; set; }
    public string Name { get; set; }
    public object Value { get; set; }
 
    public override string ToString() => $"ID: {ID}, Name: {Name}, Value: {Value}";
}
 
public partial class Program
{
    public static void SerializeStruct()
    {
        TestStruct structData = new() { ID = 2, Name = "struct", Value = 11 };
        XmlSerializer serializer = new(typeof(TestStruct));
 
        serializer.Serialize(Console.Out, structData);
    }
}
<?xml version="1.0" encoding="Codepage - 949"?>
<TestStruct xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ID>2</ID>
  <Name>struct</Name>
  <Value xsi:type="xsd:int">11</Value>
</TestStruct>
List<T>
public partial class Program
{
    public static void SerializeList()
    {
        List<TestClass> listData = new();
        for (int i = 0; i < 5; i++)
        {
            listData.Add(new() { ID = i, Name = $"Name {i}", Value = i });
        }
        XmlSerializer serializer = new(typeof(List<TestClass>));
 
        serializer.Serialize(Console.Out, listData);
    }
}
<?xml version="1.0" encoding="Codepage - 949"?>
<ArrayOfTestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TestClass>
    <ID>0</ID>
    <Name>Name 0</Name>
    <Value xsi:type="xsd:int">0</Value>
  </TestClass>
  <TestClass>
    <ID>1</ID>
    <Name>Name 1</Name>
    <Value xsi:type="xsd:int">1</Value>
  </TestClass>
  <TestClass>
    <ID>2</ID>
    <Name>Name 2</Name>
    <Value xsi:type="xsd:int">2</Value>
  </TestClass>
  <TestClass>
    <ID>3</ID>
    <Name>Name 3</Name>
    <Value xsi:type="xsd:int">3</Value>
  </TestClass>
  <TestClass>
    <ID>4</ID>
    <Name>Name 4</Name>
    <Value xsi:type="xsd:int">4</Value>
  </TestClass>
</ArrayOfTestClass>

3. Deserialize

여기서는 class, struct, List<T> 형식의 객체를 역직렬화하는 예시를 보여준다. 편의상 MemoryStream에 쓰고 바로 deserialize를 수행한다.

Class
public class TestClass
{
    public int ID { get; set; }
    public string? Name { get; set; }
    public required object Value { get; set; }
 
    public override string ToString() => $"ID: {ID}, Name: {Name}, Value: {Value}";
}
 
public partial class Program
{
    public static void DeserializeClass()
    {
        TestClass classData = new() { ID = 1, Name = "class", Value = 10 };
        XmlSerializer serializer = new(typeof(TestClass));
        Stream stream = new MemoryStream();
 
        serializer.Serialize(stream, classData);
 
        stream.Position = 0;
 
        Console.WriteLine(serializer.Deserialize(stream));
    }
}
ID: 1, Name: class, Value: 10
Struct
public struct TestStruct
{
    public int ID { get; set; }
    public string Name { get; set; }
    public object Value { get; set; }
 
    public override string ToString() => $"ID: {ID}, Name: {Name}, Value: {Value}";
}
 
public partial class Program
{
    public static void DeserializeStruct()
    {
        TestStruct structData = new() { ID = 2, Name = "struct", Value = 11 };
        XmlSerializer serializer = new(typeof(TestStruct));
        Stream stream = new MemoryStream();
 
        serializer.Serialize(stream, structData);
 
        stream.Position = 0;
 
        Console.WriteLine(serializer.Deserialize(stream));
    }
}
ID: 2, Name: struct, Value: 11
List<T>
public partial class Program
{
    public static void DeserializeList()
    {
        List<TestClass> listData = new();
        for (int i = 0; i < 5; i++)
        {
            listData.Add(new() { ID = i, Name = $"Name {i}", Value = i });
        }
        XmlSerializer serializer = new(typeof(List<TestClass>));
        Stream stream = new MemoryStream();
 
        serializer.Serialize(stream, listData);
 
        stream.Position = 0;
 
        Console.WriteLine(string.Join(Environment.NewLine, (List<TestClass>)serializer.Deserialize(stream)!));
    }
}
ID: 0, Name: Name 0, Value: 0
ID: 1, Name: Name 1, Value: 1
ID: 2, Name: Name 2, Value: 2
ID: 3, Name: Name 3, Value: 3
ID: 4, Name: Name 4, Value: 4

4. IXmlSerializable

위에 서술된 바와 같이, 기본적으로 직렬화 및 역직렬화의 대상은 객체의 public 멤버 (필드 또는 속성) 이다. 이 때, 직렬화 및 역직렬화에 대한 커스터마이징을 하고 싶은 경우 IXmlSerializable 인터페이스를 사용할 수 있다.

WARNING

해당 인터페이스 구현 시 주의해야 할 사항이 있는데, IXmlSerializable.GetSchema() 메서드는 반드시 null을 반환해야 한다.

This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the XmlSchemaProviderAttribute to the class.

Declaration
public record TestRecord : IXmlSerializable
{
    public int ID { get; set; }
    public required string Name { get; set; }
    public object? Value { get; set; }
 
    public XmlSchema? GetSchema() => null;
 
    public void ReadXml(XmlReader reader)
    {
        // Deserialize
        ID = int.Parse(reader.GetAttribute(nameof(ID))!);
        reader.Read();
        Name = reader.ReadElementContentAsString(nameof(Name), "");
    }
 
    public void WriteXml(XmlWriter writer)
    {
        // Serialize
        writer.WriteAttributeString(nameof(ID), ID.ToString());
        writer.WriteElementString(nameof(Name), Name);
    }
 
    public override string ToString() => $"ID: {ID}, Name: {Name}, Value: {Value}";
}
Serialization
public static void SerializeRecord()
{
    TestRecord record = new() { ID = 1, Name = "record", Value = "Hello" };
    XmlSerializer serializer = new(typeof(TestRecord));
 
    serializer.Serialize(Console.Out, record);
}
<?xml version="1.0" encoding="Codepage - 949"?>
<TestRecord ID="1">
  <Name>record</Name>
</TestRecord>
Deserialization
public static void DeserializeRecord()
{
    TestRecord record = new() { ID = 1, Name = "record", Value = "Hello" };
    XmlSerializer serializer = new(typeof(TestRecord));
    Stream stream = new MemoryStream();
 
    serializer.Serialize(stream, record);
 
    stream.Position = 0;
 
    Console.WriteLine(serializer.Deserialize(stream));
}
ID: 1, Name: record, Value:

5. 참조 자료