Search code examples
c#xmlxmlreader

Unable to parse XML attribute value into C# Enum in a desired way


I have the following class in C#


public class DynamicFieldParameter
{
        [XmlAttribute]
        public string ParentName { get; set; }

        [XmlAttribute]
        public string Label { get; set; }

        [XmlAttribute]
        public string ParameterName { get; set; }

        [XmlAttribute]
        public string Value { get; set; }

        [XmlAttribute("ParameterValueType")]
        public ParameterValueType ValueTypeID { get; set; }
}

Then I have the following Enum,


    [Serializable]
    public enum ParameterValueType
    {
        [XmlEnum(Name = "0")]
        Conditional,
        [XmlEnum(Name = "1")]
        Static
    }

I am trying to parse the following XML,

<?xml version="1.0" encoding="UTF-8"?>
<DynamicFormExport Version="2">
  <DynamicForm>
    <DynamicField>
      <ListParameters>
        <Parameter ParentName="spGetPickListItems" ParameterName="DisplayCode" 
             Label="Display Code" Value="3" ValueTypeID="1" />
      </ListParameters>
    </DynamicField>
  </DynamicForm>
</DynamicFormExport>

I am using the below code to parse XML,


        /// <summary>
        /// Iterates through a xml reader and loads a parameter set
        /// </summary>
        /// <param name="reader">Xml reader. Position is expected to be at the parent node of a parameter collection (e.g. ListParameters)</param>
        /// <returns></returns>
        private List<DynamicFieldParameter> LoadParametersFromXmlReader(XmlReader reader)
        {
            List<DynamicFieldParameter> parameters = new List<DynamicFieldParameter>();
            if (reader == null)
                return parameters;

            XmlReader paramReader = reader.ReadSubtree();
            paramReader.MoveToContent();
            paramReader.Read();

            string paramXml = paramReader.ReadOuterXml();
            while (!string.IsNullOrEmpty(paramXml))
            {
                parameters.Add(DynamicFieldParameter.FromXml(paramXml));
                paramXml = paramReader.ReadOuterXml();
            }

            return parameters;
        }

What I see in the debugger is that, I get ValueTypeID = Conditional even though XML I am parsing has ValueTypeID = 1, I expect my ValueTypeID to be Static.

What am I doing wrong with the parsing?


Solution

  • Below shows one way to get the desired attribute values by using System.Xml.Serialization.

    Create a class (name: HelperXml.cs) for the deserialize method.

    HelperXml.cs

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace XmlSerializationTest
    {
        public class HelperXml
        {
            public static T DeserializeXMLFileToObject<T>(string xmlFilename)
            {
                //Usage: Class1 myClass1 = DeserializeXMLFileToObject<Class1>(xmlFilename);
    
                T rObject = default(T);
                if (String.IsNullOrEmpty(xmlFilename)) return default(T);
    
                using (System.IO.StreamReader sr = new System.IO.StreamReader(xmlFilename))
                {
                    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
    
                    //get data
                    rObject = (T)serializer.Deserialize(sr);
                }
    
                return rObject;
            }
        }
    }
    

    Given the following XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <DynamicFormExport Version="2">
      <DynamicForm>
        <DynamicField>
          <ListParameters>
            <Parameter ParentName="spGetPickListItems" ParameterName="DisplayCode" 
                 Label="Display Code" Value="3" ValueTypeID="1" />
          </ListParameters>
        </DynamicField>
      </DynamicForm>
    </DynamicFormExport>
    

    Create classes for each of the following:

    • DynamicFormExport
    • DynamicForm
    • DynamicField
    • ListParameters
    • Parameter

    As far as naming strategy, in order to keep the classes in the desired order in VS, it can be helpful to do something like the following:

    • DynamicFormExport (name: XmlDynamicFormExport)
    • DynamicForm (name: XmlDynamicFormExportDynamicForm)
    • DynamicField (name: XmlDynamicFormExportDynamicFormDynamicField)
    • ListParameters (name: XmlDynamicFormExportDynamicFormDynamicFieldListParameters)
    • Parameter (name: XmlDynamicFormExportDynamicFormDynamicFieldListParametersParameter)

    Note: The class names I used is the class name above it pre-pended to the class name. (ex: XmlDynamicFormExport + DynamicForm = XmlDynamicFormExportDynamicForm). Although, the names in your XML are somewhat long, so a different naming strategy may be desirable.

    XmlDynamicFormExport.cs

    using System.Xml;
    using System.Xml.Serialization;
    
    namespace XmlSerializationTest
    {
        [XmlRoot(ElementName = "DynamicFormExport", IsNullable = false)]
        public class XmlDynamicFormExport
        {
            [XmlAttribute(AttributeName = "Version")]
            public int Version { get; set; }
    
            [XmlElement(ElementName = "DynamicForm")]
            public XmlDynamicFormExportDynamicForm DynamicForm = new XmlDynamicFormExportDynamicForm();
        }
    }
    

    XmlDynamicFormExportDynamicForm.cs

    using System.Xml;
    using System.Xml.Serialization;
    
    namespace XmlSerializationTest
    {
        public class XmlDynamicFormExportDynamicForm
        {
            [XmlElement(ElementName = "DynamicField")]
            public XmlDynamicFormExportDynamicFormDynamicField DynamicField = new XmlDynamicFormExportDynamicFormDynamicField();
        }
    }
    

    XmlDynamicFormExportDynamicFormDynamicField.cs

    using System.Xml;
    using System.Xml.Serialization;
    
    namespace XmlSerializationTest
    {
        public class XmlDynamicFormExportDynamicFormDynamicField
        {
            [XmlElement(ElementName = "ListParameters")]
            public XmlDynamicFormExportDynamicFormDynamicFieldListParameters ListParameters = new XmlDynamicFormExportDynamicFormDynamicFieldListParameters();
        }
    }
    

    XmlDynamicFormExportDynamicFormDynamicFieldListParameters.cs

    Note: Although it wasn't shown in the XML in the OP, it seems like more than 1 parameter could exist, so I used a List in the code below.

    using System.Collections.Generic;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace XmlSerializationTest
    {
        public class XmlDynamicFormExportDynamicFormDynamicFieldListParameters
        {
            [XmlElement(ElementName = "Parameter")]
            public List<XmlDynamicFormExportDynamicFormDynamicFieldListParametersParameter> Parameter = new List<XmlDynamicFormExportDynamicFormDynamicFieldListParametersParameter>();
        }
    }
    

    Option 1:

    XmlDynamicFormExportDynamicFormDynamicFieldListParametersParameter.cs

    using System.Xml;
    using System.Xml.Serialization;
    
    namespace XmlSerializationTest
    {
        public enum ParameterValueType
        {
            [XmlEnum(Name = "0")]
            Conditional,
            [XmlEnum(Name = "1")]
            Static
        }
    
        public class XmlDynamicFormExportDynamicFormDynamicFieldListParametersParameter
        {
            [XmlAttribute(AttributeName = "ParentName")]
            public string ParentName { get; set; }
    
            [XmlAttribute(AttributeName = "ParameterName")]
            public string ParameterName { get; set; }
    
            [XmlAttribute(AttributeName = "Label")]
            public string Label { get; set; }
    
            [XmlAttribute(AttributeName = "Value")]
            public int Value { get; set; }
    
            [XmlAttribute(AttributeName = "ValueTypeID")]
            public ParameterValueType ValueTypeID { get; set; }
    
        }
    }
    

    Usage (Option 1):

    string filename = @"C:\Temp\DynamicFormExport.xml";
    XmlDynamicFormExport dynamicFormExport = HelperXml.DeserializeXMLFileToObject<XmlDynamicFormExport>(filename);
    
    foreach (var p in dynamicFormExport.DynamicForm.DynamicField.ListParameters.Parameter)
    {
        System.Diagnostics.Debug.WriteLine("ParentName: " + p.ParameterName + " p.ValueTypeID: " + p.ValueTypeID.ToString());
    }
    

    Option 2:

    XmlDynamicFormExportDynamicFormDynamicFieldListParametersParameter.cs

    using System;
    using System.Xml;
    using System.Xml.Serialization;
    
    namespace XmlSerializationTest
    {
        [Serializable]
        public enum ParameterValueType : int
        {
            [XmlEnum(Name = "Conditional")]
            Conditional = 0,
            [XmlEnum(Name = "Static")]
            Static = 1
        }
    
    
        [Serializable()]
        public class XmlDynamicFormExportDynamicFormDynamicFieldListParametersParameter
        {
            [XmlAttribute(AttributeName = "ParentName")]
            public string ParentName { get; set; }
    
            [XmlAttribute(AttributeName = "ParameterName")]
            public string ParameterName { get; set; }
    
            [XmlAttribute(AttributeName = "Label")]
            public string Label { get; set; }
    
            [XmlAttribute(AttributeName = "Value")]
            public int Value { get; set; }
    
            [XmlAttribute(AttributeName = "ValueTypeID")]
            public int ValueTypeID { get; set; }
    
            //this property isn't written to XML, it's only for use in the app
            [XmlIgnore]
            public ParameterValueType ValueType
            {
                get { return (ParameterValueType)ValueTypeID; }
    
                set 
                {
                    ValueTypeID = (int)value;
                }
            }
        }
    }
    

    Usage (Option 2):

    string filename = @"C:\Temp\DynamicFormExport.xml";
    XmlDynamicFormExport dynamicFormExport = HelperXml.DeserializeXMLFileToObject<XmlDynamicFormExport>(filename);
    
    foreach (var p in dynamicFormExport.DynamicForm.DynamicField.ListParameters.Parameter)
    {
        System.Diagnostics.Debug.WriteLine("ParentName: " + p.ParameterName + " p.ValueTypeID: " + p.ValueTypeID.ToString() + " p.ValueType: " + p.ValueType.ToString());
    }