Home > Blockchain >  XmlReader stops deserializing after first array property
XmlReader stops deserializing after first array property

Time:04-19

I have a custom list used to have features of BindingList<T> while also being XML serializable. The SBindingList<T> class is as follows:

public class SBindingList<T> : BindingList<T>, IXmlSerializable
{
    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        reader.Read();
        XmlSerializer serializer = new XmlSerializer(typeof(List<T>));
        List<T> ts = (List<T>)serializer.Deserialize(reader);
        foreach (T item in ts)
        {
            Add(item);
        }
    }

    public void WriteXml(XmlWriter writer)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(List<T>));
        serializer.Serialize(writer, this.ToList());
    }
}

The idea is to read and write the XML as if it was a List<T> behind the scenes but still works and acts like a BindingList<T>. The issue I'm having is that it starts reading <Games> then when it gets to Add(item); starts to deserialize <AvailableQuests>. Because <AvailableQuests> is empty it just falls out of ReadXml but goes immediately back finishing up adding <Games> and just falls out of ReadXml without even touching <Characters> or <ActiveQuests>. The <Games>, <AvailableQuests>, <Characters>, and <ActiveQuests> are all SBindingList<T>s. Also note that I redacted all the IDs for privacy reasons. Just replace the [blah] tags with any ulong.

<Games>
    <ArrayOfGame xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <Game GuildID="[GuildID]" BotChannel="0" QuestChannel="0">
            <AvailableQuests>
                <ArrayOfQuest />
            </AvailableQuests>
            <Characters>
                <ArrayOfCharacter>
                    <Character Name="The Real Dirty Dan" Class="Meme" Level="17"
                        OwnerID="[Owner1]" Busy="false" />
                    <Character Name="Bob" Class="Builder" Level="2" OwnerID="[Owner2]"
                        Busy="false" />
                </ArrayOfCharacter>
            </Characters>
            <ActiveQuests>
                <ArrayOfQuest />
            </ActiveQuests>
        </Game>
    </ArrayOfGame>
</Games>

Here is the object setup incase someone needs it:

public class Game
{
    /// <summary>
    /// Only for serialization
    /// </summary>
    public Game() { }

    public Game(ulong guildID)
    {
        GuildID = guildID;
    }

    /// <summary>
    /// What discord server is the game on
    /// </summary>
    [XmlAttribute]
    public ulong GuildID { get; set; }

    [XmlAttribute]
    public ulong BotChannel { get; set; }

    [XmlAttribute]
    public ulong QuestChannel { get; set; }

    public SBindingList<Quest> AvailableQuests { get; set; } = new SBindingList<Quest>();

    public SBindingList<Character> Characters { get; set; } = new SBindingList<Character>();

    public SBindingList<Quest> ActiveQuests { get; set; } = new SBindingList<Quest>();

    public string Test { get; set; } // This gets ignored too

    public Character GetCharacterByName(string name)
    {
        return (from c in Characters
                where c.Name == name
                select c).FirstOrDefault();
    }
}

I don't really know where to start with this one. I've tried using a List<T> or just reading each T one at a time but both ways end up ignoring all other elements. My only guess is that I need to clean something up with the reader before I can let it fall out of ReadXml like reader.FinishedReading().

CodePudding user response:

If reader is on any other element besides the next element, the reader gives up deserizing the rest of the object. In other words, the reader must be on the next element (In this case the Character element) to continue to deserialize the <Game> tag. In this case it can be fixed by using reader.Skip() then reader.ReadEndElement()

CodePudding user response:

Try following :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication23
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Games));
            Games games = (Games)serializer.Deserialize(reader);
        }
    }
    public class Games
    {
        [XmlArray(ElementName = "ArrayOfGame")]
        [XmlArrayItem(ElementName = "Game")]
        public List<Game> game { get; set; }
 
    }
    public class Game
    {
        [XmlAttribute()]
        public string GuildID { get; set; }
        [XmlAttribute()]
        public int BotChannel { get; set; }
        [XmlAttribute()]
        public int QuestChannel { get; set; }
        [XmlArray(ElementName = "AvailableQuests")]
        [XmlArrayItem(ElementName = "ArrayOfQuest")]
        public List<Quest> availableQuest { get; set; }
        [XmlElement(ElementName = "Characters")]
        public Characters characters { get; set; }
        [XmlArray(ElementName = "ActiveQuests")]
        [XmlArrayItem(ElementName = "ArrayOfQuest")]
        public List<Quest> activeQuest { get; set; }
        public class Quest
    {
    }
    public class Characters
    {
        [XmlArray(ElementName = "ArrayOfCharacter")]
        [XmlArrayItem(ElementName = "Character")]
        public List<Character> character { get; set; }
    }
    public class Character
    {
        [XmlAttribute()]
        public string Name { get; set; }
        [XmlAttribute()]
        public string Class { get; set; }
        [XmlAttribute()]
        public int Level { get; set; }
        [XmlAttribute()]
        public string OwnerID { get; set; }
        [XmlAttribute()]
        public Boolean Busy{ get; set; }
    }
    public class ArrayOfQuest
    {
    }

}}
  • Related