Search code examples
c#jsonjson.netjsonconvert

C# Converting mixed data type in nested JSON into an object


I have a problem while developing a project, and I really need your helps !

I got a POST response using var resp = webClient.UploadString(url, header). The result looks like


{ "dataList": [{ "status":1,
                "dataOptions": {"partitions": ""}},
              { "status":1,
                "dataOptions": {"partitions": ""}},
              { "status":1,
                "dataOptions": {"partitions": [{"ref": "txtRef", 
                                               "object1": "test", 
                                               "dateFormat": "YYmmdd"}]}},
              { "status":1,
                "dataOptions": {"partitions": []}}]
}

I, then, created the object to retrieve these data as the following:

 public class testData
    {
        public object anotherData{ get; set; }
        public List<testDataCollect> dataList{ get; set; }
    }

 public class testDataCollect
    {
        public int status { get; set; }
        public dataSourceOptions dataOptions{ get; set; } 
    }

 public class dataSourceOptions
    {
        public List<Partition> partitions { get; set; }
    }

 public class Partition
    {
        [JsonProperty("ref")]
        public object Ref { get; set; }
        public string object1 { get; set; }
        public string dateFormat { get; set; }
    }

With the created objects to handle JSON response, I used

testData data = JsonConvert.DeserializeObject<testData>(response)

but at the end, I get the error message like:

Unexpected character encountered while parsing value: [. Path 'testDataCollect[3].dataSourceOptions.partitions', line 1, position 36024.

I also have tested on using dynamic variable, removing partitions variable in the objects, or use JSON to C# Converter e.g. https://json2csharp.com/ to convert the response to the objects. All of these tests still return the same error message above.

Thus, I would like to ask for suggestions or solutions of this error.

Here is the image of sample data if you might have questions about the data structure, but it can run totally fine on Python, thus I came to ask for solutions to turn the JSON into C# objects since the whole solution I tested before, it doesn't work at all. (I have changed data structure on the provided data due to the project's policies. Sorry for the inconvenience.) enter image description here Thanks


Solution

  • Based on the shape of your example JSON on the question this change in the serialization class will work:

    public class dataSourceOptions
    {
        private JToken propertyValue 
        {
            get 
            {
                return partitions.Value<JToken>();
            }
        }
         
        private JArray propertyArrayValue 
        {
            get 
            {
                return partitions.Value<JArray>();
            }
        }
         
         
        public JToken partitions { get; set; }
         
        public string stringValue {
            get 
            {
                if (propertyValue.Type == JTokenType.String) {
                    return propertyValue.Value<string>();
                }
                return null;
            }
        }
         
        public List<string> stringValues { 
            get 
            {
                if (propertyValue.Type == JTokenType.Array) 
                {
                     return propertyArrayValue.Values<JToken>().Where(t => t.Type == JTokenType.String).Select(v=> v.Value<string>()).ToList();
                } 
                return null;
            }
        }
       
        public List<Partition> partitionValues  {
            get 
            {
                if (propertyValue.Type == JTokenType.Array) {
                    return propertyArrayValue.Values<JToken>().Where(t => t.Type == JTokenType.Object).Select(v=> v.ToObject<Partition>()).ToList();
                }
                return null;
            }
        }
    }
    

    It adds three properties to the dataSourceOptions type. Each property then inspects the JToken to infer the underlying value and provide sensible values for consumers of that class. This is an example how you would use it:

    var data = JsonConvert.DeserializeObject<testData>(json);
    
    data.dataList[0].dataOptions.stringValue.Dump("string"); 
    data.dataList[1].dataOptions.stringValues[0].Dump("string array position 0");
    data.dataList[2].dataOptions.partitionValues[0].Dump("partition position 0"); 
    

    (note that Dump() is a LinqPad helper method)

    When there is no value for either of these properties, it will return null. Do add null checks in your actual code to distinguish which data you've actually received.

    Screenshot for working proof:

    json and code with dumped results