1

I need to generate a List<Event> from a XML document, and the Event has a List<Contact>. How can I convert the XML to a List<Event> object when reading:

This is my event class:

public class Event
{
    public string id { get; set; }
    public string title { get; set; }
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
    public virtual List<Contact> contacts { get; set; }
}

Contact class

public class Contact
{

    public int Id { get; set; }
    public string Name { get; set; }
}

This is how I save the XML document when the user adds something.

<Event>
    <id>1</id>
    <title>AA</title>
    <start>2019-12-01T14:13:58.863</start>
    <end>2019-12-01T15:13:58.787</end>
    <contacts>

      <contact>
        <id>1</id>
        <name>ABC</name>
      </contact>

      <contact>
        <id>2</id>
        <name>ABCD</name>
      </contact>

      <contact>
        <id>3</id>
        <name>ABCDE</name>
      </contact>

    </contacts>
  </Event>

To get the XML document I use

XDocument xml = new XDocument();
xmlDoc = XDocument.Load("Data.xml");

I'm struck on how to read the items to a list. How can I do it?

Roxana Sh
  • 294
  • 1
  • 3
  • 14
  • 1
    You can deserialize the xml: https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlserializer.deserialize?view=netframework-4.8 – Crowcoder Dec 01 '19 at 11:31

2 Answers2

3

Assuming that the root of your xml is called Events:

<?xml version="1.0" encoding="UTF-8"?>
<Events>
    <Event>
        <id>1</id>
        <title>AA</title>
        <start>2019-12-01T14:13:58.863</start>
        <end>2019-12-01T15:13:58.787</end>
        <contacts>
            <contact>
                <id>1</id>
                <name>ABC</name>
            </contact>
            <contact>
                <id>2</id>
                <name>ABCD</name>
            </contact>
            <contact>
                <id>3</id>
                <name>ABCDE</name>
            </contact>
        </contacts>
    </Event>
    <Event>
        <id>2</id>
        <title>AA</title>
        <start>2019-12-02T14:13:58.863</start>
        <end>2019-12-02T15:13:58.787</end>
        <contacts>
            <contact>
                <id>4</id>
                <name>DEF</name>
            </contact>
            <contact>
                <id>5</id>
                <name>BNH</name>
            </contact>
            <contact>
                <id>6</id>
                <name>KLK</name>
            </contact>
        </contacts>
    </Event>
</Events>

You could define the following two classes that would represent the structure of your XML document:

[XmlType("Event")]
public class Event
{
    [XmlElement("id")]
    public string Id { get; set; }
    [XmlElement("title")]
    public string Title { get; set; }
    [XmlElement("start")]
    public DateTime Start { get; set; }
    [XmlElement("end")]
    public DateTime End { get; set; }
    [XmlArray("contacts")]
    public virtual List<Contact> Contacts { get; set; }
}

[XmlType("contact")]
public class Contact
{
    [XmlElement("id")]
    public int Id { get; set; }

    [XmlElement("name")]
    public string Name { get; set; }
}

Then you could deserialize your XML as below:

using (var reader = new StreamReader(filename))
{
    var deserializer = new XmlSerializer(typeof(List<Event>), new XmlRootAttribute("Events"));
    return (List<Event>) deserializer.Deserialize(reader);
}

You have to replace the filename with the path in which your xml file resides in.

If we assume that you have selected your xml to be copied to the output directory - right click on file and check it's properties to check this or to set it -, then you can get it's path fairly easily as below (assuming that it is called events.xml):

var outputDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var filename = Path.Combine(outputDirectory, @"events.xml");

In order you get in grips with XmlSerializer, I would suggest you start from XmlSerializer and continue with this Examples of XML Serialization.

Christos
  • 53,228
  • 8
  • 76
  • 108
  • 1
    Hi, this worked, and thanks for the links I'll check those out, I'm new to c# and everything seems totally strange. – mike oconner Dec 01 '19 at 13:33
  • Can i use the same to get a specific node from the xml file and update and save it – mike oconner Dec 01 '19 at 14:16
  • @mikeoconner You are very welcome. I am glad that I helped. – Christos Dec 01 '19 at 15:31
  • @mikeoconner Had I have to get a specific node, update it and save it, I would have gone with LINQ to XML. You could start by reading about it at https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml-overview. And then look answers like this https://stackoverflow.com/questions/1487653/update-xml-with-c-sharp-using-linq or check for tutorials, they are plenty of them. If you need any specific help. Please ask another question stating specifically what's the problem. Thanks – Christos Dec 01 '19 at 15:34
1

Use System.Xml.Serialization to save/load the Event object to/from XML. The format of the XML that will be created is very similar to yours.

Saving/loading a List<Event> can be done in the same way (in the exmapls below, Event needs to be replaced with List<Event>).

Note: The load (using XmlSerializer.Deserialize) in the below example will not work properly with the XML file you created, it works only with an XML file created by the save example (using XmlSerializer.Serialize). To use XmlSerializer.Deserialize with your own XML file will require adding XmlElement attributes to the class properties and providing an XmlRootAttribute to the XmlSerializer (as in Christos answer). If you can create the XML file with XmlSerializer.Serialize and not use the file you already created you will save yourself extra work.

Example of saving the Event object to XML (serializing):

public static void Save(string fileName, Event e)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Event));

    using (Stream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
    {

        xmlSerializer.Serialize(stream, e);
    }
}

Example of loading the Event object from XML (desalinizing):

public static Event Load(string fileName)
{
    Event e = null;
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Event));

    using (Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read,  FileShare.None))
    {

        e = (Event)xmlSerializer.Deserialize(stream);
    }

    return e;
}

The XML that will be created for an Event object similar (or identical) to what you have in the file:

<?xml version="1.0"?>
<Event xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <id>1</id>
  <title>AA</title>
  <Start>2019-12-01T14:13:58.863</Start>
  <End>2019-12-01T15:13:58.787</End>
  <contacts>
    <Contact>
      <Id>1</Id>
      <Name>ABC</Name>
    </Contact>
    <Contact>
      <Id>2</Id>
      <Name>ABCD</Name>
    </Contact>
    <Contact>
      <Id>3</Id>
      <Name>ABCDE</Name>
    </Contact>
  </contacts>
</Event>
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
  • I tried this method, first time it gave me a error saying that the RootTag was not expected. then I found an answer on stackoverflow saying to initialize the serializer like XmlSerializer xmlSerializer = new XmlSerializer(typeof(List),xRoot); – mike oconner Dec 01 '19 at 16:35
  • Once I did that the code was executed but the List was empty – mike oconner Dec 01 '19 at 16:35
  • The default operation of `xmlSerializer.Deserialize` will not work correctly with your file. What I meant in the answer is to suggest you to create the XML files with the `XmlSerializer.Serialize` in the first place, this way your object will be serialized to an XML in the default way and `xmlSerializer.Deserialize` will work correctly. If you want to work with the file you already created, you will have to use `XmlElement` attributes like in [Christos](https://stackoverflow.com/users/913124/christos) [answer](https://stackoverflow.com/a/59125239/10927863) – Eliahu Aaron Dec 01 '19 at 16:45
  • And you will need to provide the `XmlRootAttribute` like in [Christos](https://stackoverflow.com/users/913124/christos) answer. If you create the file in the first place with `XmlSerializer.Serialize` than you will have less work, no need for `XmlElement` attributes and `XmlRootAttribute`. – Eliahu Aaron Dec 01 '19 at 16:57