-1

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

Boonrak K.
  • 11
  • 1
  • 2
  • Looks like syntax error in the json. You're going to have to post a more representative sample of the json you are parsing. Not just a snippet that 'looks like' what you're parsing, but an actual minimal example that we can try and parse ourselves. Also, you json does not validate, please post valid json. Also, you have a class called `dataSourceOptions` but reference it (?) as `dataOptions`; what is it supposed to be? – Jonathan Jul 08 '22 at 05:10
  • Dear @Jonathan, Syntax in the JSON has no error since I try saving to a JSON file and testing it on Python, and it works totally fine. This, I think the JSON syntax has no problem, but the data inside do. ```dataSourceOptions``` is my mistakes on parsing the code to the blog, but the error still the same. – Boonrak K. Jul 08 '22 at 05:21
  • Are you 100% sure that the partitions property that has the array thing is correct? Because that what is inside that array isn't valid JSON. It is missing `{ } ` if that is meant to be an object. That still leaves us with the question: What does the response payload look like around position 36024, say +/- 100 characters. – rene Jul 08 '22 at 06:24
  • @rene Ok, I'm missing again. Sorry for the inconvenience. Currently, I've fixed the sample data and provided an image of sample data (I cannot provide the whole sample of data structure due to our project's policies, but if there is any further questions you have, please ask and I'll provide as much as possible to lead to the solutions. Thanks – Boonrak K. Jul 08 '22 at 06:57
  • Based on your screenshot I now infer that partitions also can be an array of strings. But your example also shows partitions to be just a single string. C# is extremely ill-suited for strong-typing a collection that can't make its mind up. – rene Jul 08 '22 at 07:03
  • @rene Thanks for you advise. I also think the issue might be about the types of data inside since when I checked with Python for the obj[0:3] has partitions in string format while the obj[3] has partition in array, but the actual problem is I cannot interfere with the server response. Thus, I tried ignore this partitions object by deleting it from the class structure, but it doesn't work. :'( (I, currently, try replacing string inside the response to force it to work, but if you have any solutions, please let me know. Thanks again for your kind information) – Boonrak K. Jul 08 '22 at 07:18
  • I also tried applying this solution on my code, but it still get the same error :'< [https://stackoverflow.com/questions/18994685/how-to-handle-both-a-single-item-and-an-array-for-the-same-property-using-json-n] – Boonrak K. Jul 08 '22 at 08:39

1 Answers1

1

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

rene
  • 41,474
  • 78
  • 114
  • 152