0

I am trying to deserialize JSON by reading a file. This file will be the same as what I will receive in future requests. I created classes based on some sample data. However, I am running into trouble with property names that do not appear on every response. Therefore, I receive an exception which says

"ArgumentException: Could not cast or convert from System.String to object" error.  

For example, below is a sample of the JSON I am trying to deserialize. My classes are build for this layout

      "contents": [
        {
          "template": "paragraph",
          "title": null,
          "values": [
            {
              "name": null,
              "value": "paragraph of text"
            },
            {
              "name": null,
              "value": "paragraph of text"
            }
          ]
        }
]

However sometimes I get this as a response.

      "contents": [
        {
          "template": "paragraph",
          "title": null,
          "values": [
            "paragraph of text"
          ]
        }
]

Notice how the { } and properties "name" and "value" are gone from the "values" array?

Here is my class for the "contents" array

    public class Contents
    {
        public Contents()
        {
            Values = new List<TemplateValue>();
        }

        [JsonPropertyName("template")]
        public string Template { get; set; }

        [JsonPropertyName("title")]
        public string Title { get; set; }

        [JsonPropertyName("values")]
        public List<TemplateValue> Values { get; set; }
    }

    public class TemplateValue
    {
        [JsonPropertyName("name")]
        public string Name { get; set; }

        [JsonPropertyName("value")]
        public string Value { get; set; }
    }

Here is how I am deserializing the file

var jsonString = File.ReadAllText(_jsonFilelocation);
var jsonObject = JsonConvert.DeserializeObject<Contents>(jsonString);

How can I adjust my code to fix this type of scenario? Thanks in advance for any help!

Sathish Guru V
  • 1,417
  • 2
  • 15
  • 39
jayz
  • 3
  • 2
  • Try this solution as suggested here, which may convert your null strings to an empty strings https://stackoverflow.com/questions/23830206/json-convert-empty-string-instead-of-null – Sathish Guru V Jan 31 '20 at 17:55
  • Get the provider to give you a definition of the content. – fredrik Jan 31 '20 at 17:55
  • Thanks for the replies. I don't think that link will solve my issue. Technically, "values" isn't null. If the JSON is missing the properties as mentioned above, I'll still need to grab the data of the "values" property. I think I may be looking for a way of implementing some type of dynamic JSON property in my class, if it's even an option. – jayz Jan 31 '20 at 19:20
  • @jayz, so `values` may sometimes be an array of objects, and sometimes an array of strings? or is one of these options represents a malformed response structure? – OfirD Feb 02 '20 at 12:51
  • @HeyJude, thanks for the reply. It may be a malformed response structure. There are multiple instances of this in the test json file I am using, sometimes its strings and sometimes its objects. I am trying to be proactive and have my code ready if a malformed response isn't the issue. However, I am leaning more and more that way as I can't find any solutions that would solve this type of issue – jayz Feb 03 '20 at 13:22
  • @jayz, so if you stumble a malformed response on runtime, you still want to handle it, instead of just ignoring it? – OfirD Feb 03 '20 at 13:26
  • @HeyJude, No I don't want to ignore any error. I just can't confirm that this is actually a malformed response. I have reached out to the vender that is providing the json and I am awaiting a response. I just have a hunch that they are going to tell me its not malformed causing me to have to deal with it somehow. – jayz Feb 03 '20 at 14:25
  • @jayz, I think you can try what's done [here](https://stackoverflow.com/questions/53326193/how-to-custom-deserialize-into-an-object-with-json-net), and if it won't work for you, I'll upload an answer later. – OfirD Feb 03 '20 at 16:47

1 Answers1

0

Build a dedicated converter for your TemplateValue class:

public class TemplateValueConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(TemplateValue);
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        var templateValue = new TemplateValue();
        if (token.Type == JTokenType.Object)
        {
            serializer.Populate(token.CreateReader(), templateValue);
        }
        else if (token.Type == JTokenType.String)
        {
            templateValue.Value = (string)token;
        }
        else
        {
            throw new JsonException($"Unexpected token type for TemplateValue: { token.Type.ToString() }");
        }
        return templateValue;
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary (would be neccesary if used for serialization)");
    }
}

... and you can then handle both jsons:

var json1 = @"
[
    {
        ""template"": ""paragraph"",
        ""title"": null,
        ""values"": [{
            ""name"": null,
            ""value"": ""paragraph of text""
        }]
    }
]";
var json2 = @"
[
    {
        ""template"": ""paragraph"",
        ""title"": null,
        ""values"": [
            ""paragraph of text""
        ]
    }
]";
var contents1 = JsonConvert.DeserializeObject<Contents[]>(json1, new TemplateValueConverter());
var contents2 = JsonConvert.DeserializeObject<Contents[]>(json2, new TemplateValueConverter());
OfirD
  • 9,442
  • 5
  • 47
  • 90
  • Thanks @HeyJude for the link in the above comments and the example posted above. Between the 2, I should be able to make this work. Seems the best way to tackle this. Thanks again! – jayz Feb 04 '20 at 17:00