Adam  M.
Adam M.

Reputation: 1203

Deserialize xml into list of objects

I am attempting to deserialize some xml into a list of objects.

Where I am running into trouble is that the secondary objects Resources within the Event object doesn't have the values populated. For a little more background, this is a unity project, I'm deserializing a list of random events that can happen.

Below is the XML I am testing on:

<?xml version='1.0'?>
<Events>
  <Event>
    <Description>
      this is a test
    </Description>
    <Reward>
        <Food>
          0
        </Food>
        <Energy>
          1
        </Energy>
        <Happiness>
          2
        </Happiness>
        <ShipHp>
          3
        </ShipHp>
        <Garbage>
          4
        </Garbage>
    </Reward>
    <Cost>
        <Food>
          0
        </Food>
        <Energy>
          1
        </Energy>
        <Happiness>
          2
        </Happiness>
        <ShipHp>
          3
        </ShipHp>
        <Garbage>
          4
        </Garbage>
    </Cost>
    <EventType>
      Good
    </EventType>
    <EventId>
      1
    </EventId>
  </Event>
  <Event>
    <Description>
      this is a test
    </Description>
    <Reward>
        <Food>
          0
        </Food>
        <Energy>
          1
        </Energy>
        <Happiness>
          2
        </Happiness>
        <ShipHp>
          3
        </ShipHp>
        <Garbage>
          4
        </Garbage>
    </Reward>
    <Cost>
        <Food>
          0
        </Food>
        <Energy>
          1
        </Energy>
        <Happiness>
          2
        </Happiness>
        <ShipHp>
          3
        </ShipHp>
        <Garbage>
          4
        </Garbage>
    </Cost>
    <EventType>
      Good
    </EventType>
    <EventId>
      1
    </EventId>
  </Event>
</Events>

Here are the Objects (Event and Resources):

public class Event {
    [XmlElement("Description")]
    public string Description { get; set; }

    [XmlElement("Reward")]
    public Resources Reward { get; set; }

    [XmlElement("Cost")]
    public Resources Cost { get; set; }

    [XmlElement("EventId")]
    public int EventId { get; set; }

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

}

[XmlType("Resources")]
public class Resources {

    //Each prop can be extended to food being a Food object with expiry, etc
    [XmlElement("Food")]
    public int Food { get; set; } // 0 to cap
    [XmlElement("Happiness")]
    public int Happiness { get; set; } // 0 to 100
    [XmlElement("Energy")]
    public int Energy { get; set; } //0 to cap
    [XmlElement("ShipHp")]
    public int ShipHp { get; set; }// cap to 0
    [XmlElement("Garbage")]
    public int Garbage { get; set; } // 0 to cap
}

And here is the deserialzation code I have:

   public class EventHandler : MonoBehaviour
{

    [XmlInclude(typeof(Event))]
    [XmlInclude(typeof(Resources))]
    [Serializable, XmlRoot("Events")]
    public class EventData
    {
        [XmlElement("Event")]
        public List<Event> Events { get; set; }

        public EventData()
        {
            List<Event> Events = new List<Event>();
        }
    }

    List<Event> Events = new List<Event>();
    public static EventHandler instance;

    // Use this for initialization, called on script enabling
    void Start()
    {

        var serializer = new XmlSerializer(typeof(EventData));
        try
        {
            string xml = File.ReadAllText("Assets/TextResources/Events.xml");
            xml = xml.Replace("\n", string.Empty);
            xml = xml.Replace("\r", string.Empty);
            xml = xml.Replace("\t", string.Empty);
            using (var stringReader = new StringReader(xml))
            {
                using (var reader = XmlReader.Create(stringReader))
                {
                    var result = (EventData)serializer.Deserialize(reader);
                    Events = result.Events;

                }
            }
        }
        catch(Exception ex)
        {
            Debug.Log(ex);
        }
    }
}

so from this code, my Event Objects are being populated with the values for Description, EventType and EventId, but Reward and Cost Resources Objects are not being populated with the values and so far, I am at a loss why this is the case. For bonus points, I also am struggling to find a way to simply remove all of the dead space that I get from parsing a string. (ie Good gets changed to "\n\r Good ").

Upvotes: 0

Views: 1522

Answers (2)

Adam  M.
Adam M.

Reputation: 1203

OK. So I found a solution to the problem, I'm no expert with any of this as so if there is any corrections more experienced people want to add to improve the code I'll be happy to make changes.

First of all, there was a formating issue with my XML, To find this I actually serialized a list of event objects and used the resulting text to find a solution to that, Below is the revised text:

<ArrayOfEvent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Event>
    <Description>abc</Description>
    <Reward>
      <Food>0</Food>
      <Happiness>2</Happiness>
      <Energy>1</Energy>
      <ShipHp>3</ShipHp>
      <Garbage>4</Garbage>
    </Reward>
    <Cost>
      <Food>0</Food>
      <Happiness>2</Happiness>
      <Energy>1</Energy>
      <ShipHp>3</ShipHp>
      <Garbage>4</Garbage>
    </Cost>
    <EventId>1</EventId>
    <EventType>Good</EventType>
  </Event>
  <Event>
    <Description>def</Description>
    <Reward>
      <Food>0</Food>
      <Happiness>2</Happiness>
      <Energy>1</Energy>
      <ShipHp>3</ShipHp>
      <Garbage>4</Garbage>
    </Reward>
    <Cost>
      <Food>0</Food>
      <Happiness>2</Happiness>
      <Energy>1</Energy>
      <ShipHp>3</ShipHp>
      <Garbage>4</Garbage>
    </Cost>
    <EventId>2</EventId>
    <EventType>Good</EventType>
  </Event>
</ArrayOfEvent>

so first thing to notice is that I serialized a list of event objects, this the <ArrayOfEvent> tag and the xml Name Space attributes that were generated. from that. this also meant that I had to change from the EventData class to just a List<Event> which was more convenient any way. as from my Event and Resources classes, I added the [Serializable, XmlRoot()] tag to their classes. One other thing to note is that i swapped EventType from a string to an enum, the code for that enum will be provided below as well These Fixes also fixed the formatting issues i was getting from the xml plain text to XmlReader I will share the complete code below for future readers:

[Serializable, XmlRoot("Event")]
public class Event
{
    [XmlElement("Description")]
    public string Description { get; set; }

    [XmlElement("Reward")]
    public Resources Reward { get; set; }

    [XmlElement("Cost")]
    public Resources Cost { get; set; }

    [XmlElement("EventId")]
    public int EventId { get; set; }

    [XmlElement("EventType")]
    public EventType EventType { get; set; }
    [XmlIgnore]
    public string MarkUp { get; set; }
}


[Serializable, XmlRoot("Resources")]
public class Resources
{
    //Each prop can be extended to food being a Food object with expiry, etc
    [XmlElement("Food")]
    public int Food { get; set; } // 0 to cap
    [XmlElement("Happiness")]
    public int Happiness { get; set; } // 0 to 100
    [XmlElement("Energy")]
    public int Energy { get; set; } //0 to cap
    [XmlElement("ShipHp")]
    public int ShipHp { get; set; }// cap to 0
    [XmlElement("Garbage")]
    public int Garbage { get; set; } // 0 to cap
}

[XmlType("EventType")]
public enum EventType
{
    [XmlEnum("Good")]
    Good = 0,
    [XmlEnum("Bad")]
    Bad,
    [XmlEnum("Neutral")]
    Neutral
}


public class EventHandler : MonoBehaviour
{

    List<Event> Events = new List<Event>();

    // Use this for initialization, called on script enabling
    void Start()
    {
        try
        {
            var serializer = new XmlSerializer(typeof(List<Event>));

            string xml = File.ReadAllText("Assets/TextResources/Events.xml");
            xml = XDocument.Parse(xml).ToString(SaveOptions.DisableFormatting);

            using (var stringReader = new StringReader(xml))
            {
                using (var reader = XmlReader.Create(stringReader))
                {
                    var result = (List<Event>)serializer.Deserialize(reader);
                    Events = result;
                }
            }
        }
        catch (Exception ex)
        {
            Debug.Log(ex);
        }
    }
}

Basic serializer method

private void serializeList()
{
    XmlSerializer ser = new XmlSerializer(typeof(List<Event>));
    List<Event> list = new List<Event>();
    list.Add(new Event { EventId = 1, Description = "abc", EventType = EventType.Good, Cost = new Resources { Food = 0, Energy = 1, Happiness = 2, ShipHp = 3, Garbage = 4 }, Reward = new Resources { Food = 0, Energy = 1, Happiness = 2, ShipHp = 3, Garbage = 4 } });
    list.Add(new Event { EventId = 2, Description = "def", EventType = EventType.Good, Cost = new Resources { Food = 0, Energy = 1, Happiness = 2, ShipHp = 3, Garbage = 4 }, Reward = new Resources { Food = 0, Energy = 1, Happiness = 2, ShipHp = 3, Garbage = 4 } });
    list.Add(new Event { EventId = 3, Description = "ghi", EventType = EventType.Good, Cost = new Resources { Food = 0, Energy = 1, Happiness = 2, ShipHp = 3, Garbage = 4 }, Reward = new Resources { Food = 0, Energy = 1, Happiness = 2, ShipHp = 3, Garbage = 4 } });
    StreamWriter sw = new StreamWriter("Assets/TextResources/test.xml");
    ser.Serialize(sw, list);
    sw.Close();
}

Upvotes: 1

Eric
Eric

Reputation: 1847

Your XML is invalid. Your first Resources tag is never closed.

Upvotes: 0

Related Questions