Search code examples
c#arraysjsongraphqlmonday.com

C# Parsing Json string returned from GraphQL - Monday.com


I'm running into issues parsing a json reponse from GraphQL. The issue is the array will come back with more arrays half the time. My code is just getting out of hand and ugly.

Json file (trimmed it a bit. It can be 20+ data arrays)

{
  "activity_logs": [
    {
      "data": "{\"board_id\":2165785546,\"group_id\":\"new_group2051\",\"is_top_group\":false,\"pulse_id\":2165787062,\"pulse_name\":\"Tyler\",\"column_id\":\"email_address\",\"column_type\":\"email\",\"column_title\":\"Email Address\",\"value\":{\"column_settings\":{\"includePulseInSubject\":true,\"ccPulse\":true,\"bccList\":\"\"}},\"previous_value\":{\"email\":\"[email protected]\",\"text\":\"[email protected]\",\"changed_at\":\"2022-02-15T21:18:48.297Z\",\"column_settings\":{\"includePulseInSubject\":true,\"ccPulse\":true,\"bccList\":\"\"}},\"is_column_with_hide_permissions\":false,\"previous_textual_value\":\"[email protected]\"}"
    },
    {
      "data": "{\"board_id\":2165785546,\"group_id\":\"new_group2051\",\"is_top_group\":false,\"pulse_id\":216578711,\"pulse_name\":\"Nicholas \",\"column_id\":\"email_address\",\"column_type\":\"email\",\"column_title\":\"Email Address\",\"value\":{\"column_settings\":{\"includePulseInSubject\":true,\"ccPulse\":true,\"bccList\":\"\"}},\"previous_value\":{\"email\":\"[email protected]\",\"text\":\"[email protected]\",\"changed_at\":\"2022-02-16T04:44:52.046Z\",\"column_settings\":{\"includePulseInSubject\":true,\"ccPulse\":true,\"bccList\":\"\"}},\"is_column_with_hide_permissions\":false,\"previous_textual_value\":\"[email protected]\"}"
    },
    {
      "data": "{\"board_id\":2165785546,\"group_id\":\"new_group2051\",\"is_top_group\":false,\"pulse_id\":216578711,\"pulse_name\":\"Nicholas \",\"column_id\":\"batch_5\",\"column_type\":\"text\",\"column_title\":\"Batch #\",\"value\":{\"value\":\"75\"},\"previous_value\":{\"value\":\"74\"},\"is_column_with_hide_permissions\":false}"
    },
    {
      "data": "{\"board_id\":2165785546,\"group_id\":\"new_group2051\",\"pulse_id\":216578711,\"is_top_group\":false,\"value\":{\"name\":\"Nicholas \"},\"previous_value\":{\"name\":\"Nicholas \"},\"column_type\":\"name\",\"column_title\":\"Name\"}"
    }
  ]
}

Random "get it to work" attempt after giving up on making is a List based on a Class. The IContainers within IContainers were getting very complex.

var responseData = JObject.Parse(responseText).SelectToken("data").SelectToken("boards").SelectToken("activity_logs");
dynamic updatedRecords = JsonConvert.DeserializeObject(responseData.ToString());

foreach (var record in updatedRecords)
{
List<Dictionary<string, string>> records = new List<Dictionary<string, string>>();
Dictionary<string, string> fields = new Dictionary<string, string>();
dynamic updates = JsonConvert.DeserializeObject(JObject.Parse(record.ToString()).SelectToken("data").ToString());
foreach(var update in updates)
{
    switch (update.Name.ToString())
    {
        case "column_id":
            fields.Add(update.Name.ToString(), update.Value.ToString());
            break;
        case "pulse_name":
            fields.Add(update.Name.ToString(), update.Value.ToString());
            break;
        case "value":
            dynamic values = JsonConvert.DeserializeObject(JObject.Parse(update.Value.ToString()));
            if (update.Name.ToString().Contains("column_settings"))
            {
                foreach (var value in values)
                {
                    dynamic columns = JsonConvert.DeserializeObject(JObject.Parse(value.Value.ToString()));
                    foreach(var column in columns)
                    {
                        fields.Add($"Value_{column.Name.ToString()}", column.Value.ToString());
                    }
                                                
                }
            }
            else
            {
                foreach (var value in values)
                {
                    fields.Add($"Value_{value.Name.ToString()}", value.Value.ToString());
                }
            }
                                        
            break;
        case "previous_value":
            dynamic prevValues = JsonConvert.DeserializeObject(JObject.Parse(update.Value.ToString()));
            foreach (var prevalue in prevValues)
            {
                fields.Add($"Prevalue_{prevalue.Name.ToString()}", prevalue.Value.ToString());
            }
            break;
        case "previous_textual_value":
            fields.Add(update.Name.ToString(), update.Value.ToString());
            break;
    }
}
if (fields.Count > 0)
{
    records.Add(fields);
    fields.Clear();
}
}

My Error when I get to:

dynamic values = JsonConvert.DeserializeObject(JObject.Parse(update.Value.ToString()));
-       $exception  {"The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject(string)' has some invalid arguments"} Microsoft.CSharp.RuntimeBinder.RuntimeBinderException

Solution is a big help and led to my answer. The issue is the activity_logs data comes with escape characters in it so the string contains \\". I had to format the data sections with Replace("\\", "") and Replace("\"{", "{") and Replace("}\""), "}"). This made the string readable as a Json file.


Solution

  • You have to pass in a string to DeserializeObject instead of a JSON object.

    Another way would be get your JSON mapped to a POCO types as follows, easy way to do is on Visual Studio (Copy your JSON contents, on Visual Studio create a new empty class -> Edit-> Past Special -> Paste JSON as classes)

    public class LogsRoot
    {
        public Activity_Logs[] activity_logs { get; set; }
    }
    
    public class Activity_Logs
    {
        public string data { get; set; }
    }
    
    public class DataRoot
    {
        public long board_id { get; set; }
        public string group_id { get; set; }
        public bool is_top_group { get; set; }
        public long pulse_id { get; set; }
        public string pulse_name { get; set; }
        public string column_id { get; set; }
        public string column_type { get; set; }
        public string column_title { get; set; }
        public Value value { get; set; }
        public Previous_Value previous_value { get; set; }
        public bool is_column_with_hide_permissions { get; set; }
        public string previous_textual_value { get; set; }
    }
    
    public class Value
    {
        public Column_Settings column_settings { get; set; }
    }
    
    public class Column_Settings
    {
        public bool includePulseInSubject { get; set; }
        public bool ccPulse { get; set; }
        public string bccList { get; set; }
    }
    
    public class Previous_Value
    {
        public string email { get; set; }
        public string text { get; set; }
        public DateTime changed_at { get; set; }
        public Column_Settings1 column_settings { get; set; }
    }
    
    public class Column_Settings1
    {
        public bool includePulseInSubject { get; set; }
        public bool ccPulse { get; set; }
        public string bccList { get; set; }
    }
    

    Then load the JSON and manipulate as follows,

    var json = File.ReadAllText("data.json");
    var rootLogs = JsonConvert.DeserializeObject<LogsRoot>(json);
    
    Dictionary<string, string> fields = new Dictionary<string, string>();
    
    foreach (var logJson in rootLogs.activity_logs)
    {
        var log = JsonConvert.DeserializeObject<DataRoot>(logJson.data);
        fields.Add(log.column_id, log.value.column_settings.bccList + log.value.column_settings.ccPulse);
        fields.Add(log.pulse_name, log.value.column_settings.bccList + log.value.column_settings.ccPulse);
        fields.Add(log.previous_value.email, log.value.column_settings.bccList + log.value.column_settings.ccPulse);
        fields.Add(log.previous_textual_value, log.value.column_settings.bccList + log.value.column_settings.ccPulse);
    }