1

Xml example:

<Profile>
  <ProfileSections>
    <ProfileSection Name="Default">
      <Email>test@test.com</Email>
      <Pasword>test</Pasword>
    </ProfileSection>
    <ProfileSection Name="Address">
      <Street>Undermissed</Street>
    </ProfileSection>
  </ProfileSections>
</Profile>

How to deserialize this XML to have List ProfileSections and each profile section to necessary object? Object examples:

public class DefaultProdileSection{
        [XmlAttribute]
        public string Name{ get; set; }
        public string Email{ get; set; }
        public string PAssword{ get; set; }
}

public class AddressProdileSection{
            [XmlAttribute]
            public string Name{ get; set; }
            public string Street{ get; set; }
 }
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • Does this answer your question? [How to Deserialize XML document](https://stackoverflow.com/questions/364253/how-to-deserialize-xml-document) – pritaeas Apr 25 '22 at 13:11
  • 1
    @pritaeas Unfortunately, no. The problem is that types of objects in ProfileSections are different and I do not know how to define which type to use during deserialization. Usually it's 'type' tag, but there is no such tag in my xml. – this_is_fine_bro Apr 25 '22 at 13:25
  • 'type' attribute, not tag – this_is_fine_bro Apr 25 '22 at 13:31
  • 2
    Easiest thing is probably to deserialize `` to some DTO with all possible properties, and then postprocess into your class hierarchy after deserialization. Can you infer the schema for the `` elements from the value for the `Name` element? Does the name attribute have some fixed set of values? – dbc Apr 25 '22 at 13:34
  • Seems yes, the easiest way. – this_is_fine_bro Apr 25 '22 at 13:55
  • Yes, name has fixed set of values, and the type of object depends on this value. – this_is_fine_bro Apr 25 '22 at 13:56

1 Answers1

1

We can manually add a type attribute to the xml token stream when parsing.

If you really really want to use deserialization, then I can suggest the following code.

Set of classes:

public class Profile
{
    public List<ProfileSection> ProfileSections { get; set; }
}

[XmlInclude(typeof(DefaultProfileSection))]
[XmlInclude(typeof(AddressProfileSection))]
public class ProfileSection
{
    [XmlAttribute]
    public string Name { get; set; }
}

public class DefaultProfileSection : ProfileSection
{
    public string Email { get; set; }
    public string Password { get; set; }
}

public class AddressProfileSection : ProfileSection
{
    public string Street { get; set; }
}

A custom xml reader that will add the necessary information on the fly:

public class TypeAddingXmlReader : XmlTextReader
{
    public TypeAddingXmlReader(string url) : base(url) { }

    // Define the remaining constructors here

    public override string? GetAttribute(string localName, string? namespaceURI)
    {
        if (base.LocalName == "ProfileSection" &&
            localName == "type" &&
            namespaceURI == "http://www.w3.org/2001/XMLSchema-instance")
        {
            var name = base.GetAttribute("Name");

            return name switch
            {
                "Default" => nameof(DefaultProfileSection),
                "Address" => nameof(AddressProfileSection)
                // add other class names here
            };
        }

        return base.GetAttribute(localName, namespaceURI);
    }
}

Use:

var ser = new XmlSerializer(typeof(Profile));

using var reader = new TypeAddingXmlReader("test.xml");

var profile = (Profile)ser.Deserialize(reader);

foreach (var ps in profile.ProfileSections)
    Console.WriteLine(ps.Name + " " + ps.GetType());

Namespaces:

using System.Xml;
using System.Xml.Serialization;

Note: but I would use linq to xml and not sweat it.

Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49