Resolved Deserialize XML to Object - Main Property Names are in the XML Element

jmooney5115

New member
Joined
Oct 16, 2018
Messages
4
Programming Experience
5-10
Hi. I am having an issue figuring out the best way to deserialize the below XML into an easily usable object. Using a C# to XML converter I have a usable object in which I can serialize and deserialize objects. The issue is that using this XML I have to use LINQ to pull out property values. The main property names I am after are stored in an XML element called Name. Deserializing the XML will give me a list of Settings. The setting contains 2 properties, name & value; LINQ is the only way I know to get the values.

I have a wrapper class that takes the deserialized object and has a property for each of the 'Setting' property in the XML. The getter/setter for properties in the wrapper contains the LINQ required. There has to be a better way to do this right?

Wrapper object with only the property Description.

C#:
public class ObjectWrapper{    public string Description
    {
        get
        {
            var result = "";

            try
            {
                if (obj is null) return result;

                var columns = obj.Setting.Where(q => q.Name == "Description").ToList();

                if (columns.Count == 1)
                    result = columns.Single().Value.ToString();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failed to parse Description obj. Ex: " + ex.ToString());
            }

            return result;
        }
        set
        {
            if (string.IsNullOrEmpty(value)) return;

            try
            {
                if (obj is null) return;

                var columns = obj.Setting.Where(q => q.Name == "Description").ToList();

                if (columns.Count == 1)
                    columns.Single().Value = value;
                else
                {
                    //if Name doesn't exist, create it and assign it.
                    var Name = new Setting
                    {
                        Name = "Description",
                        Value = value
                    };

                    obj.Setting.Add(Name);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Failed to parse Description obj. Ex: " + ex.ToString());
            }
        }
    }
}


XML

C#:
<Object>
  <Processor>
    <Pages>
      <Page>
        <Name>Settings</Name>
        <Row>
          <Setting>
            <Name>Enable</Name>
            <Value>True</Value>
          </Setting>
          <Setting>
            <Name>Name</Name>
            <Value>value</Value>
          </Setting>
          <Setting>
            <Name>Description</Name>
            <Value>value</Value>
          </Setting>
        </Row>
        <Row>
          <Setting>
            <Name>Enable</Name>
            <Value>True</Value>
          </Setting>
          <Setting>
            <Name>Name</Name>
            <Value>value</Value>
          </Setting>
          <Setting>
            <Name>Description</Name>
            <Value>value</Value>
          </Setting>
        </Row>
      </Page>
    </Pages>
  </Processor>
</Object>




Plugged the above code into this site to generate XML: https://xmltocsharp.azurewebsites.net/


C#:
/*     Licensed under the Apache License, Version 2.0
    
[URL]http://www.apache.org/licenses/LICENSE-2.0[/URL]
*/
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
namespace Xml2CSharp
{
    [XmlRoot(ElementName="Setting")]
    public class Setting {
        [XmlElement(ElementName="Name")]
        public string Name { get; set; }
        [XmlElement(ElementName="Value")]
        public string Value { get; set; }
    }

    [XmlRoot(ElementName="Row")]
    public class Row {
        [XmlElement(ElementName="Setting")]
        public List<Setting> Setting { get; set; }
    }

    [XmlRoot(ElementName="Page")]
    public class Page {
        [XmlElement(ElementName="Name")]
        public string Name { get; set; }
        [XmlElement(ElementName="Row")]
        public List<Row> Row { get; set; }
    }

    [XmlRoot(ElementName="Pages")]
    public class Pages {
        [XmlElement(ElementName="Page")]
        public Page Page { get; set; }
    }

    [XmlRoot(ElementName="Processor")]
    public class Processor {
        [XmlElement(ElementName="Pages")]
        public Pages Pages { get; set; }
    }

    [XmlRoot(ElementName="Object")]
    public class Object {
        [XmlElement(ElementName="Processor")]
        public Processor Processor { get; set; }
    }
}
 
Last edited:
You can paste the xml here Xml2CSharp.com | Convert your XML Examples into XmlSerializer compatable C# Classes as you said and copy the generated classes to a new Code File (empty .cs file).
Note that it need accurate xml data to generate collection properties, for example if the xml has multiple Page objects the sample file needs that too for the converter to see it. Here the sample has a single Page object and that is converted to a single item property.

Then you can deserialize the xml like in this example: How to: Read Object Data from an XML File (C#) | Microsoft Docs for type Xml2CSharp.Object.

Now you can loop through the rows and settings:
foreach (var row in overview.Processor.Pages.Page.Row)
{
    foreach (var setting in row.Setting)
    {
        //setting.Name 
        //setting.Value
    }
}

I used the 'overview' variable name from the deserialization example for easier reference.

There also exist a .Net tool called xsd.exe that can do similar, but it may not work out of the box and may generate far more complicated schema/classes. This is also the same as 'Paste Xml As Classes' option in VS. I can reveal that for your sample xml it generated just a mess in my opinion.
 
Thanks for the reply. You have given me an idea here that I can iterate through each row/setting and use reflection to get property values. This should work better than hundreds of lines of LINQ to get property values.


I'm inheriting a project and I believe they used xsd.exe to generate the C# from XML. You're right that it generated FAR more complicated classes.
 
Why use reflection? That's typically used when you have string with a property name and want to get the PropertyInfo and its value from an object dynamically, here you have an object with these known properties already.
 
Why use reflection? That's typically used when you have string with a property name and want to get the PropertyInfo and its value from an object dynamically, here you have an object with these known properties already.


Maybe I'm missing something. The code doesn't know to split out the properties (Enable, Name, & Description) that exist as a Setting in each row. Each row contains many settings. The setting contains the property name and the value of the property.

I created a sample project to illustrate how this looks. https://github.com/jmooney5115/funkyXmlSerialization

Pl4h2PL.png




What I want:

  • Object
    • Processor
      • Pages
        • Page
          • Row
            • string Enabled
            • string Name
            • string Description

What I get:
  • Object
    • Processor
      • Pages
        • Page
          • Row
            • Setting
              • string Name = Enabled
              • string Value = True
            • Setting
              • string Name = Name
              • string Value = jmooney
            • Setting
              • string Name = Description
              • string Value = user on csharpforums.net

 
I see. That is the problem of the xml structure, not the code. To get "what you want" without work the xml data should have been like this:
HTML:
        <Row>
            <Enable>True</Enable>
            <Name>value</Name>
            <Description>value</Description>
        </Row>
Otherwise you have to read the xml file and manually create the objects of your own defined classes. For example let's say you only wanted the rows and defined this class:
public class Row
{
    public string Enable { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

'Enable' looks like it should be a boolean by the way.

Then you read the xml and create the objects:
var doc = XDocument.Load(@"d:\sample.xml");
var rows = new List<Row>();
foreach (var row in doc.Descendants("Row"))
{
    var row = new Row();
    foreach (var setting in row.Elements("Setting"))
    {
        var name = setting.Element("Name").Value;
        var value = setting.Element("Value").Value;
        row.GetType().GetProperty(name).SetValue(row, value);
    }
    rows.Add(row);
}

Here I used reflection in just one line, but could have used a switch statement for different handling of each property, for example to set row.Enable = Convert.ToBoolean(value);
 
Thank you for the detailed reply. I should have mentioned I'm dealing with 3rd party XML which I cannot modify. It sounds like I need to keep doing what I'm doing.
 
Back
Top Bottom