Tom Sawyer
Tom Sawyer

Reputation: 857

How to deserialize XML with repeating tag name with unique identifier as attribute?

I am trying to deserialize this xml where all the node has the same name with a unique identifier as an attribute.

<configuration>
    <setting name="host">
        <value>127.0.0.1</value>
    </setting>
    <setting name="port">
        <value>80</value>
    </setting>
</configuration>

The result I am trying to achieve is:

public class Configuration
{
    string host { get; set; }
    int port { get; set; }
}

I read the previous question and I am still stumbling with the fact they have the same tag name.

Thank you!

Upvotes: 1

Views: 373

Answers (3)

EdSF
EdSF

Reputation: 12351

You can call this "old school" but it works.

  1. Copy the XML (or fragment, etc) and leverage Visual Studio (2015(?) and up - shot below is 2017 ) feature "Paste XML/JSON As Classes"

    Visual Studio 2017

    This helps tremendously with valid XML - particularly with the "proper" attributes that decorate the generated classes. Additionally, it's just a class, so you can customize it as you deem fit (while retaining the attributes).

    For more complex XML - such as namespaces/prefixes, you'll really appreciate this. If you don't have this tool, you can use XSD.exe (even more old school) - does the same thing for XML documents.

  2. Auto-generated Classes from above step:

    ...stumbling with the fact they have the same tag name...

    Don't be. XML elements can repeat - often do (e.g. sitemap.xml of every web site out there). The generated class will help you grok it. It's a standard collection/array/list.

    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
    public partial class configuration
    {
    
        private configurationSetting[] settingField;
    
        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute("setting")]
        public configurationSetting[] setting
        {
            get
            {
                return this.settingField;
            }
            set
            {
                this.settingField = value;
            }
        }
    }
    
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class configurationSetting
    {
    
        private string valueField;
    
        private string nameField;
    
        /// <remarks/>
        public string value
        {
            get
            {
                return this.valueField;
            }
            set
            {
                this.valueField = value;
            }
        }
    
        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string name
        {
            get
            {
                return this.nameField;
            }
            set
            {
                this.nameField = value;
            }
        }
    }
    

Given the above, you can do this:

string rawXml = "<configuration><setting name=\"host\"><value>127.0.0.1</value></setting><setting name=\"port\"><value>80</value></setting></configuration>";

var ser = new XmlSerializer(typeof(configuration));
configuration config;
using (TextReader rdr = new StringReader(rawXml))
{
    config = (configuration)ser.Deserialize(rdr);
}


foreach (configurationSetting setting in config.setting)
{
    Console.WriteLine($"{setting.name} = {setting.value}");
}

Output:

host = 127.0.0.1
port = 80

Hth..

Upvotes: 2

Rafael Carvalho
Rafael Carvalho

Reputation: 2046

Try this:

        XmlDocument doc = new XmlDocument();
        doc.LoadXml("yourxmlhere");

        Configuration configuration = new Configuration();
        XmlNode root = doc.FirstChild;
        if (root.HasChildNodes)
        {
            foreach (XmlNode item in root.ChildNodes)
            {
                configuration = SetValueByPropertyName(configuration, item.Attributes["name"].Value, item.FirstChild.InnerText);
            }
        }

The helper method to set values:

public static TInput SetValueByPropertyName<TInput>(TInput obj, string name, string value)
        {
            PropertyInfo prop = obj.GetType().GetProperty(name);
            if (null != prop && prop.CanWrite)
            {
                if (prop.PropertyType != typeof(String))
                {
                    prop.SetValue(obj, Convert.ChangeType(value, prop.PropertyType), null);
                }
                else
                {
                    prop.SetValue(obj, value, null);
                }
            }
            return obj;
        }

Upvotes: 1

Any Moose
Any Moose

Reputation: 648

Load your xml as a doc

XmlDocument doc = new XmlDocument();
doc.LoadXml(your_xml_data);

Then iterate through the child nodes

Configuration configuration = new Configuration();
XmlNode root = doc.FirstChild;

//fill the configuration from the child nodes.
if (root.HasChildNodes)
{
 if(root.ChildNodes[0].Name == "host")
 {
    configuration.host = root.ChildNodes[0].InnerText;
 }
 if(root.ChildNodes[1].Name == "port")
 {   
    configuration.port = root.ChildNodes[1].InnerText;
 }
}

Upvotes: 1

Related Questions