4

Is there a way to declare a class where for a specific variable I can receive either a List or a string?

I trying to deserialize a JSON and it can come in one of the formats below:

"MercadoriasPresencaCarga": {
  "Mercadoria": 7693066,
  "Descarga": "08/07/2017 13:35:39"
},

or

 "MercadoriasPresencaCarga": {
  "Mercadoria": [
    "7693066"
  ],
  "Descarga": [
    "08/07/2017 13:35:39"
  ]
},

The class for this block is created like this:

public class MercadoriasPresencaCarga
{
    public List<string> Mercadoria { get; set; }

    public List<string> Descarga { get; set; }
}

The problem is that if this block of JSON come as the first format that I showed where it is not a array, it will cause an error on it deserialization.

How could I solve this problem?

Jairo Franchi
  • 367
  • 1
  • 2
  • 13
  • 2
    Try to deserialise it to one class (representing one of the JSON structures). If it fails, try and deserialise it to the other (representing the other). – mjwills Nov 09 '18 at 11:58
  • Can you use dynamic? https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic – tomsky Nov 09 '18 at 11:59
  • Is that just a property in the middle of a bigger json string? – Rui Jarimba Nov 09 '18 at 12:04
  • dynamic wont work, because when you come to access it you wont know whether it is a string or a list (or infact you wont know what it is at all it could be anything) – Dave Nov 09 '18 at 12:09
  • Could you do some custom deserilsation and if it is a single item just add it to the list? – Dave Nov 09 '18 at 12:10
  • 4
    Also just to note, whoever made that JSON is a bad perosn – Dave Nov 09 '18 at 12:10
  • The output JSON is not good or not practical in real world scenario. What you can do is deserialize the JSON and check the length of your keys and further manipulate it – Prakash Nov 09 '18 at 12:12
  • @RuiJarimba yes, it is part of a bigger json – Jairo Franchi Nov 09 '18 at 12:37
  • @Dave I totally agree, I already requested to change it and keep a pattern. – Jairo Franchi Nov 09 '18 at 12:38
  • If you are using Json.NET, this looks like a duplicate of [How to handle both a single item and an array for the same property using JSON.net](https://stackoverflow.com/q/18994685/3744182) and/or [How to handle json that returns both a string and a string array?](https://stackoverflow.com/q/22052430/3744182). Agree? – dbc Jan 23 '19 at 07:02

1 Answers1

3

Ideally the json should always come in the same format, but if that's not a possibility there are some workarounds.

Both json strings will deserialize successfully using the following class:

public class Model
{
    // other properties here 
    // ....

    [JsonIgnore]
    public string Mercadoria => GetValue("Mercadoria");

    [JsonIgnore]
    public string Descarga => GetValue("Descarga");

    public JObject MercadoriasPresencaCarga { get; set; }


    private string GetValue(string path)
    {
        if (MercadoriasPresencaCarga == null)
        {
            return null;
        }

        string value = null;
        JToken token = MercadoriasPresencaCarga.SelectToken(path);

        if (token.Type == JTokenType.Array && token.HasValues)
        {
            value = token.First.Value<string>();
        }
        else
        {
            value = token.Value<string>();
        }

        return value;
    }
}

Please note that:

  • MercadoriasPresencaCarga will be deserialized as JObject
  • Both Mercadoria and Descarga are non-serializable properties (marked with [JsonIgnore])

Testing the code - json string with string properties (no arrays):

string json1 = @"{
    ""MercadoriasPresencaCarga"": {
      ""Mercadoria"": 7693066,
      ""Descarga"": ""08/07/2017 13:35:39""
    }
}";

Model model1 = JsonConvert.DeserializeObject<Model>(json1);

Console.WriteLine($"Descarga: {model1.Descarga}, Mercadoria: {model1.Mercadoria}");

Testing the code - json string with arrays:

string json2 = @"{
    ""MercadoriasPresencaCarga"": {
      ""Mercadoria"": [
        ""7693066""
      ],
      ""Descarga"": [
        ""08/07/2017 13:35:39""
      ]
    }
}";

Model model2 = JsonConvert.DeserializeObject<Model>(json2);

Console.WriteLine($"Descarga: {model2.Descarga}, Mercadoria: {model2.Mercadoria}");
Rui Jarimba
  • 11,166
  • 11
  • 56
  • 86
  • 1
    Thanks @RuiJarimba , it will be really useful for me, I requested to who did this to keep a pattern but if it doesn't happens, at least I have a way to solve this problem. – Jairo Franchi Nov 09 '18 at 15:11
  • 1
    @JairoFranchi no problem. Yes my solution should be used as a last resort – Rui Jarimba Nov 09 '18 at 15:17