Search code examples
c#xmlxmlserializer

How to de-serialise a list with multiple xsi:type


I'm using the XmlSerializer in System.Xml.Serialization.

I have a list (or two lists really) separated by xsi:type.

<?xml version="1.0" encoding="utf-8"?>
<ButikerOmbud xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Info>
        <Meddelande>blah blah</Meddelande>
    </Info>
    <ButikOmbud xsi:type="StoreAssortmentViewModel">
        <Typ>Butik</Typ><Nr>2515</Nr>
    </ButikOmbud>
    <ButikOmbud xsi:type="StoreAssortmentViewModel">
        <Typ>Butik</Typ><Nr>2516</Nr>
    </ButikOmbud>
    <ButikOmbud xsi:type="AgentAssortmentViewModel">
        <Typ>Ombud</Typ><Nr>011703-91A</Nr>
    </ButikOmbud>
    <ButikOmbud xsi:type="AgentAssortmentViewModel">
        <Typ>Ombud</Typ><Nr>011703-92B</Nr>
    </ButikOmbud>
</ButikerOmbud>

I've created some classes that map to this:

[XmlRoot(ElementName = "ButikerOmbud")]
public class ButiksCollection
{
    [XmlElement(ElementName = "Info")]
    public Info Info { get; set; }

    [XmlElement(ElementName = "ButikOmbud")]
    public List<Butik> Butiker { get; set; }
}

[XmlRoot(ElementName = "ButikOmbud")]
[XmlType(TypeName = "StoreAssortmentViewModel")]
public class Butik
{
    [XmlElement(ElementName = "Typ")]
    public string Typ { get; set; }

    [XmlElement(ElementName = "Nr")]
    public int Nr { get; set; }

}

and then I'll do this

(ButiksCollection)(new XmlSerializer(typeof(ButiksCollection)).Deserialize(memoryStream));

This should work if only the StoreAssortmentViewModel existed. But given that there exists AgentAssortmentViewModel under the same node. I'm not sure how I should de-serialise this. I'm assuming there should be another collection List<Butik> Agents on ButiksCollection.

The only attribute I've found that seems to map to xsi:type is applied to classes, which I don't think is what I want here

How do I arrange and attribute my classes so this will de-serialize?

Here's all that on dotnetfiddle: https://dotnetfiddle.net/vmT4SK


Solution

  • Try following :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                XmlReader reader = XmlReader.Create(FILENAME);
                XmlSerializer serializer = new XmlSerializer(typeof(ButiksCollection));
                ButiksCollection butik = (ButiksCollection)serializer.Deserialize(reader);
                List<StoreAssortmentViewModel> storeAssortments = butik.Butiker.Where(x => x.GetType() == typeof(StoreAssortmentViewModel)).Select(x => (StoreAssortmentViewModel)x).ToList();
                List<AgentAssortmentViewModel> agentAssortments = butik.Butiker.Where(x => x.GetType() == typeof(AgentAssortmentViewModel)).Select(x => (AgentAssortmentViewModel)x).ToList();
    
            }
        }
        [XmlRoot(ElementName = "ButikerOmbud")]
        public class ButiksCollection
        {
            [XmlElement(ElementName = "Info")]
            public Info Info { get; set; }
    
            [XmlElement(ElementName = "ButikOmbud")]
            public List<Butik> Butiker { get; set; }
        }
    
        [XmlRoot(ElementName = "ButikOmbud")]
        [XmlInclude(typeof(StoreAssortmentViewModel))]
        [XmlInclude(typeof(AgentAssortmentViewModel))]
        public class Butik
        {   
            [XmlElement(ElementName = "Typ")]
            public string Typ { get; set; }
            [XmlElement(ElementName = "Nr")]
            public string Nr { get; set; }
    
        }
        public class Info
        {
            public string Meddelande { get; set; }
        }
        public class StoreAssortmentViewModel : Butik
        {
        }
        public class AgentAssortmentViewModel : Butik
        {
        }
    }