Search code examples
c#reflectionfieldinfo

Dynamically reading class


so I have came across making few programs, that build specific XMLs with data from database.

Now i would like to make application, which I could just hang over structured class with values and it would be able to dynamically read it.

My idea of that class is something like this

public class Order
{
    public string PO_NO { get; set; }
    ................
    public List<OrderDetails> OrderDetails = new List<OrderDetails>();
}

and XML would go out as follows

<ORDER>

<PO_NO>18060001</PO_NO>
....
   -<ORDER_DETAILS>

      -<ORDER_DETAIL>
      </ORDER_DETAIL>     
      -<ORDER_DETAIL>
      </ORDER_DETAIL>
    ...
   </ORDER_DETAILS>
-</ORDER>

I hope this covers the structure and the idea.

So far I am struggling on dynamically reading the class.

The last and I quess the closest attempt is this:

foreach (var refer in DatabaseData.getRefs()) //just gets reference numbers for me, for further reading
            {
//THIS one returns the filled class. Eg. the Order class explained higher.
                var MyClass = DatabaseData.read(refer); 
                var fiel = DatabaseData.read(refer).GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
                foreach (var field in fiel)
                {
                    var fieldName = field.Name.Replace('<', ' ').Replace('>', ' ').Replace("__", null).Replace("kBackingField", null);

                    if (field.FieldType.ToString().Contains("List"))
                        Debug.WriteLine("its a list");
                    else
                        Debug.WriteLine((string)DatabaseData.read(refer).GetType().GetField(fieldName).GetValue(Activator.CreateInstance(DatabaseData.read(refer).GetType())));
                }
            }

This is the part that troubles me. I have tried many variations of this I found on forums, but I am still getting the. Object reference not set to instance of an object.

(string)DatabaseData.read(refer).GetType().GetField(fieldName).GetValue(Activator.CreateInstance(DatabaseData.read(refer).GetType()))

Any ideas please? Or point me the right way.

Thanks in advance.


Solution

  • There is no need to do anything yourself here; just use XmlSerializer.

    You might need to add some attributes to get the XML you want:

    [XmlRoot("ORDER")]
    public class Order
    {
        [XmlElement("PO_NO")] // note this one is implicit and not strictly needed
        public string PO_NO { get; set; }
        [XmlArray("ORDER_DETAILS")]
        [XmlArrayItem("ORDER_DETAIL")]
        public List<OrderDetails> OrderDetails {get;} = new List<OrderDetails>();
    }
    

    then:

    var serializer = new XmlSerializer(typeof(Order));
    

    Not that it matters once you've done this, but there's also need to use Roslyn backing field info in this case - just look at both GetProperties() and GetFields(), and using PropertyInfo.SetValue(...) instead of FieldInfo.SetValue(...) when appropriate; meaning: use the property, not the backing field.