Home > Software design >  C# Deserialize XML file to List
C# Deserialize XML file to List

Time:01-19

I want to deserialize XML into a list of objects.

This are my top level C# classes:

[XmlRoot("Events")]
[XmlType("Events")]
[Serializable]
public class WinEventLogList
{

    public WinEventLogList() { Items = new List<WinEventLog>(); }

    [XmlElement("Event")]
    public List<WinEventLog> Items { get; set; }

}

[XmlRoot("Event")]
[XmlType("Event")]
[Serializable]
public class WinEventLog
{

    [XmlElement("System")]
    [JsonPropertyName("system")]
    public EventSystem System { get; set; } = new EventSystem();

    [XmlElement("EventData")]
    [JsonPropertyName("eventData")]
    public EventData EventData { get; set; } = new EventData();

}

This is an example XML I need to deserialize:

<?xml version="1.0" encoding="utf-8"?>
<Events>
  <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
    <System>Omitted tags go in here</System>
    <EventData>Omitted tags go in here</EventData>
  </Event>
  <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
    <System>Omitted tags go in here</System>
    <EventData>Omitted tags go in here</Event>
  <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
    <System>Omitted tags go in here</System>
    <EventData>Omitted tags go in here</EventData>
  </Event>
  <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
    <System>Omitted tags go in here</System>
    <EventData>Omitted tags go in here</EventData>
  </Event>
  <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
    <System>Omitted tags go in here</System>
    <EventData>Omitted tags go in here</EventData>
  </Event>
</Events>

This are 2 different approaches I tried, both from this stackoverflow question.

First, try using the wrapper class WinEventLogList

using var stream = File.OpenRead("path/to/my/xml/file.xml");
using var reader = new StreamReader(stream);
var serializer = new XmlSerializer(typeof(WinEventLogList));
var list = serializer.Value.Deserialize(reader) as WinEventLogList;
var winEventLogs = list.Items;

Then, try using a normal List directly in the serializer.

using var stream = File.OpenRead("path/to/my/xml/file.xml");
using var reader = new StreamReader(stream);
var serializer = new XmlSerializer(
    typeof(List<WinEventLog>),  
    new XmlRootAttribute("Events"));
var winEventLogs = serializer.Value.Deserialize(reader) as List<WinEventLog>;

In both cases, the resulting winEventLogs variable is an empty list. Why does this happen? How to correct it?

CodePudding user response:

One oft-overlooked component of XML is namespaces. They are actually quite integral in the [de]serialisation process.
You were almost there, just had to specify the namespace in your object to match the XML.

Here's a working snippet:

void Main() {
  using var reader = new StringReader(xmlStr);
  var serializer = new XmlSerializer(typeof(WinEventLogList));
  var list = (WinEventLogList)serializer.Deserialize(reader)!;
  list.Dump();
}

[XmlRoot("Events")]
public class WinEventLogList {
  // Namespaces are extremely important!
  [XmlElement("Event", Namespace="http://schemas.microsoft.com/win/2004/08/events/event")]
  public List<WinEventLog> Items { get; set; } = new();
}

public class WinEventLog {
  [XmlElement] public int System { get; set; }
  [XmlElement] public int EventData { get; set; }
}

string xmlStr = """
<?xml version='1.0' encoding='utf-8'?>
<Events>
  <Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
    <System>1</System>  <EventData>2</EventData>
  </Event>
  <Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'>
    <System>3</System>  <EventData>4</EventData>
  </Event>
</Events>
""";

  • Related