XmlElement filter by attribute

936 views Asked by At

Background: I have a simple XML sheet, which i cannot change, this is supplied by a 3rd party, The class and deserialization is maintained by us

Question How do i get the xml to deserialize properly, it obviously needs to somehow check the "name" attribute to decide which property to fill, but for the life of me i can not remember how to do this. The current serializer we are using is 'System.Xml.Serialization.XmlSerializer' and due to legacy, dependent code we can not change this.

XML

<result>
    <rowset name="divisions" key="accountKey" columns="accountKey,description">
      <row accountKey="1000" description="Division 1"/>
      <row accountKey="1001" description="Division 2"/>
      <row accountKey="1002" description="Division 3"/>
      <row accountKey="1003" description="Division 4"/>
      <row accountKey="1004" description="Division 5"/>
      <row accountKey="1005" description="Division 6"/>
      <row accountKey="1006" description="Division 7"/>
    </rowset>
    <rowset name="walletDivisions" key="accountKey" columns="accountKey,description">
      <row accountKey="1000" description="Wallet Division 1"/>
      <row accountKey="1001" description="Wallet Division 2"/>
      <row accountKey="1002" description="Wallet Division 3"/>
      <row accountKey="1003" description="Wallet Division 4"/>
      <row accountKey="1004" description="Wallet Division 5"/>
      <row accountKey="1005" description="Wallet Division 6"/>
      <row accountKey="1006" description="Wallet Division 7"/>
    </rowset>
</result>

Class

[Serializable]
[XmlRoot("result", IsNullable = false)]
public class TestClass
{
    [XmlElement("rowset")]
    public EveXmlRowCollection<Division> Divisions { get; set; }
    [XmlElement("rowset")]
    public EveXmlRowCollection<Division> WalletDivisions { get; set; }

    [Serializable]
    [XmlRoot("row")]
    public class Division
    {
        [XmlAttribute("accountKey")]
        public int AccountKey { get; set; }

        [XmlAttribute("description")]
        public string Description { get; set; }
    }
}
public class EveXmlRowCollection<T> : Collection<T>, IXmlSerializable 
{
    //... Other, Irrelevant implementations

    public void ReadXml(XmlReader reader) {
        var serializer = new XmlSerializer(typeof (T));
        if (!reader.IsStartElement()) return;
        RowSetMeta.Name = reader.GetAttribute("name");
        RowSetMeta.Key = reader.GetAttribute("key");
        RowSetMeta.Columns = reader.GetAttribute("columns");
        reader.ReadToDescendant("row");
        while (reader.Name == "row") {
            if (reader.IsStartElement()) {
                var row = (T) serializer.Deserialize(reader);
                Items.Add(row);
            }
            reader.ReadToNextSibling("row");
        }
    }
}
1

There are 1 answers

4
Aleksandr Ivanov On

What about doing it in two steps? First you can deserialize xml without any logic. Just create appropriate models:

[XmlRoot("result")]
public class Result
{
    [XmlElement("rowset")]
    public DivisionSet[] DivisionSets { get; set; }
}

public class DivisionSet
{
    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlElement("row")]
    public Division[] Divisions { get; set; }
}

public class Division
{
    [XmlAttribute("accountKey")]
    public int AccountKey { get; set; }

    [XmlAttribute("description")]
    public string Description { get; set; }
}

Using them deserilizing is very trivial:

var serializer = new XmlSerializer(typeof(Result));
using (TextReader reader = new StringReader("...XML..."))
{
    var result = (Result)serializer.Deserialize(reader);
}

And then you can create needed models using C# power:

public class DeserializedAndNormilizedObject
{
    public IEnumerable<Division> Divisions { get; set; }
    public IEnumerable<Division> WalletDivisions { get; set; }
}

var obj = new DeserializedAndNormilizedObject();
var devisionsSet = result.DivisionSets.FirstOrDefault(x => x.Name == "divisions");
if (devisionsSet != null)
{
    obj.Divisions = devisionsSet.Divisions;
}
// Same for walletDivisions

And you will return DeserializedAndNormilizedObject as a result of deserialization.