C# reference Error while setting value using GetType().GetTypeInfo().GetDeclaredProperty for existing Property

309 views Asked by At

I have a relatively simple console app (framework) which has a tightly coupled classes with properties and my main triggers an async task. These are my property classes:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net.Http;                      //for HTTP client
using System.Reflection;
using System.Threading.Tasks;               //for Async Request/Response
using Newtonsoft.Json;                      //for JSON properties

namespace PSD2
{
    [Serializable]
    public class PS
    {
        public System.Uri BaseURL { get; set; } = new System.Uri("http://192.999.999.999:8888/some/url/here/");
        public ConsHeaders Headers { get; set; }
        public ConsBody Body { get; set; }
        public consAttributes Attributes { get; set; }
    }

    [Serializable]
    public partial class ConsHeaders
    {
        [JsonProperty("Core-ID")]
        public string corID { get; set; }

        [JsonProperty("PS-Token")]
        public string PS_Token { get; set; }

        [JsonProperty("Req-ID")]
        public string reqID { get; set; }
    }

    [Serializable]
    public partial class consAttributes
    {
        [JsonProperty("consId")]
        public string consId { get; set; } = String.Empty;

        [JsonProperty("consStatus")]
        public string consStatus { get; set; } = String.Empty;

        [JsonProperty("userName")]
        public string userName { get; set; } = String.Empty;
    };

    [Serializable]
    public partial class consBody
    {
        [JsonProperty("access")]
        public AccessHeaders access { get; internal set; }

        [JsonProperty("combinedServiceIndicator")]
        public Boolean CombinedServiceIndicator { get; set; } = false;

        [JsonProperty("frequencyPerDay")]
        public int FrequencyPerDay { get; set; } = 4;

        [JsonProperty("recurringIndicator")]
        public Boolean RecurringIndicator { get; set; } = false;

        [JsonProperty("validUntil")]
        public string ValidUntil { get; set; } = "9999-12-31";
    }
...

Now, my Program class creates an object and in Main I call a class called testing who has my logic behind, nothing more than populating the object properties with values, and calls a Task asycn which is also present inside. Code continues from above as:

    public class Program
    {
        public static PS cnsnt = new PS();

        public static void Main(string[] args)
        {
            Testing test = new Testing();
            test.createCONS();
        }

        public class Testing
        {
            public void createCONS()
            {
                try
                {
                    cnsnt.Headers = new ConsHeaders
                    {
                        corID = "Something",
                        reqID = "AnotherThing",
                        PS_Token = "myTokenValue"
                    };

                    cnsnt.Body = new ConsBody
                    {
                        access = new AccessHeaders
                        {
                            AllPsd2 = "allAccounts",
                            AvailableAccounts = "YES"
                        },
                        CombinedServiceIndicator = false,
                        FrequencyPerDay = 10,
                        ValidUntil = "2020-12-31"
                    };

                    cnsnt.Attributes = new consAttributes
                    {
                        consId = "",
                        _links_self_href = "",
                        consStatus = "",
                        status_href = "",
                        userName = ""
                    };
                }
                catch (System.Exception e)
                {
                    throw new System.Exception("Error - Aborting..");
                }

                myConsAsync(cnsnt.BaseURL, cnsnt, HttpMethod.Post).GetAwaiter().GetResult();
            }


            public async static Task myConsAsync(Uri HTTPaddress, PS ConSent, HttpMethod httpMethod)
            {
                try
                {
                    HttpClient client = new HttpClient();
                    System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);

                    using (HttpRequestMessage request = new HttpRequestMessage(httpMethod, HTTPaddress))
                    {
                        client.BaseAddress = HTTPaddress;
                        client.DefaultRequestHeaders.Accept.Clear();
                        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

                        client.DefaultRequestHeaders.Add("Connection", "keep-alive");
                        client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
                        //...
                        client.DefaultRequestHeaders.Add("Core-ID", ConSent.Headers.corID);
                        client.DefaultRequestHeaders.Add("Req-ID", ConSent.Headers.reqID);
                        client.DefaultRequestHeaders.Add("PS-Token", ConSent.Headers.PS_Token);
                        //...

                        request.Content = new StringContent(JsonConvert.SerializeObject(ConSent, Formatting.Indented), utf8, "application/json");

                        using (HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(false))
                        {
                            response.EnsureSuccessStatusCode();

                            Int32 code = (Int32)response.StatusCode;
                            string responseBody = response.Content.ReadAsStringAsync().Result.ToString();

                            try
                            {
                                if (responseBody.Contains("consId"))
                                {
                                    try
                                    {
                                        string responseValues = JSONtoKeyValue(responseBody);

                                        var dict = responseValues.Split('|')
                                            .Select(x => x.Split('='))
                                            .Where(x => x.Length > 1 
                                                && !String.IsNullOrEmpty(x[0].Trim())
                                                && !String.IsNullOrEmpty(x[1].Trim()))
                                            .ToDictionary(x => x[0].Trim(), x => x[1].Trim());

                                        foreach (KeyValuePair<string, string> entry in dict)
                                        {
                                            if (entry.Value == null)
                                            {
                                                dict.Remove(entry.Key);
                                            }
                                            else
                                            {
                                                string key = entry.Key;
                                                string value = entry.Value;


                                                try
                                                {
                                                    if (cnsnt.Attributes.GetType().GetTypeInfo().GetDeclaredProperty(key) != null)
                                                    {
                                                        // ---> ERROR:  Object reference not set to an instance of an object.
                                                        cnsnt.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(cnsnt, entry.Value);
                                                    }
                                                }
                                                catch (System.Exception e)
                                                {
                                                    Console.WriteLine("Failed during processing Property: " + e.Message);
                                                }
                                            }
                                        }
                                        Console.ReadLine();
                                    }
                                    catch (System.Exception e)
                                    {
                                        Console.WriteLine(e.StackTrace + "\r\n" + e.Message);
                                    }
                                }
                                else
                                {
                                    throw new System.Exception("Fatal error reading response body for the consent Id. Aborting..");
                                };
                            }
                            catch (System.Exception e)
                            {
                                Environment.Exit(13);
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    //whatever, Console.WriteLine("Error in " + e.TargetSite + "\r\n" + e.Message + "\r\n" + e.Data);
                }
                //return 
            }

            // this works as intended.. included just for completion
            public static string JSONtoKeyValue(string pString)
            {
                pString.Trim();
                if (pString == null)
                {
                    return "";
                }
                else
                {
                    pString = pString.Replace("\r\n", "|").Replace(":", "=").Replace("\"", "").Replace("{", "").Replace("}", "").Replace(",", "");
                    int j = 0, inputlen = pString.Length;
                    char[] newarr = new char[inputlen];
                    for (int i = 0; i < inputlen; ++i)
                    {
                        char tmp = pString[i];
                        if (!char.IsWhiteSpace(tmp))
                        {
                            newarr[j] = tmp; ++j;
                        }
                    }
                    return new String(newarr, 0, j).Replace("||", "|").Replace("||", "|").Replace("=|", "_").Trim('|');
                }
            }
        }
    }
}

Notice in the Task that I want to read a string separated with pipes (a small method does the work nicely) and I try to see if I have this property in my object, and if yes, to populate the value.

However, in line ConSent.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(cnsnt, entry.Value); I get the error "Object reference not set to an instance of an object."

I struggle on this one, could someone help me?

1

There are 1 answers

3
Razvan M On

You have made a simple mistake.

You check for

(cnsnt.Attributes.GetType().GetTypeInfo().GetDeclaredProperty(key) != null)

But then you assign with

ConSent.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(cnsnt, entry.Value)

Just replace it with,

cnsnt.GetType().GetTypeInfo().GetDeclaredProperty(key).SetValue(cnsnt, entry.Value).

Note its cnsnt. Not ConSent.

and you'll be fine. Happy reflecting!

edit:

saw your edit, same thing.

cnsnt.Attributes.GetType().GetTypeInfo().GetDeclaredProperty(key) cnsnt.GetType().GetTypeInfo().GetDeclaredProperty(key)

you are basically checking on a different bject if it has a property and then you try to set it on another.

Suggestion.

Why not go:

var keyProperty = cnsnt.Attributes.GetType().GetTypeInfo().GetDeclaredProperty(key);
if(keyProperty != null)
{
    keyProperty.SetValue(cnsnt, entry.Value);
}

this way it will not fail, ever.