Error when serializing JSON in C#, apparently the API returns different structures depending on the object’s completion

Asked

Viewed 60 times

-1

I’m having problems returning a third party API (I don’t have access to the code) that I’m consuming, more specifically in the section below:

var result = client.SendAsync(request).Result;
var resultContent = result.Content.ReadAsStringAsync().Result;

if (result.StatusCode == HttpStatusCode.OK)
{
    return JsonConvert.DeserializeObject<PaymentDTO>(resultContent);
}

The error occurs in the return when I deserializo the json resultContent in my object PaymentDTO.

This is my PaymentDTO:

public class PaymentDTO
{
    public IEnumerable<ItemDTO> Itens { get; set; }

    [...]
}

This is my ItemDTO:

public class ItemDTO
{
   [...]

   public RecordDTO Cadastro { get; set; }
}

This is my RecordDTO:

public class RecordDTO
{
    [...]
    
    public AddressDTO Endereco { get; set; }
}

And this is mine AddressDTO :

public class AddressDTO
{
    public string Id { get; set; }
    public string Cep { get; set; }
    public string Logradouro { get; set; }
    public string Numero { get; set; }
    public string Complemento { get; set; }
    public string Bairro { get; set; }
    public string Cidade { get; set; }
    public string Uf { get; set; }
}

The problem occurs when returning the API when the address object changes, it changes according to whether the person has a registered address or not. Below is an example of a return for a person with an address:

"itens": [
    {
      "id": "1193053",
      [...]
      "cadastro": {
        "id": "230009543",
        "nome_completo": "xxxx xxx",
        "endereco": {
          "id": "151032",
          "cep": "xxxxxx",
          "logradouro": "RUA XXXX",
          "numero": "777",
          "complemento": "blá blá",
          "bairro": "bairro tal",
          "cidade": "XXXXX",
          "uf": "SP"
        }
      }
    }
]

Now an example of a return for a person with no address registered in the API:

"itens": [
  {
    "id": "879432",
    "cadastro": {
        "id": "43264",
        "nome_completo": "xxxx xxx",
        "endereco": []
    }
  }
]

You can tell when the person no address, the object endereco is returned as an array: endereco: [] instead of being returned as endereco: null, for example. But when the person has address, the object is not returned as endereco: [{...}]. When the person has address, it is returned as a single object. This causes, at the time of deserializar, if the person has no address, the process bursts the following exception:

Cannot deserialize the Current JSON array (e.g. [1,2,3]) into type 'Domnomedomeuassembly.DTO.Addressdto' because the type requires a JSON Object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON Object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that Implements a Collection interface (e.g. Icollection, Ilist) like List that can be deserialized from a JSON array. Jsonarrayattribute can also be Added to the type to force it to deserialize from a JSON array. Path 'items[1].Address', line 671, position 29. |

Because, I imagine, Newtonsoft’s deserializer understands that the object endereco returned is an array, but it is not. So the error asks me to define my object AddressDTO as a list. Example:

public class RecordDTO
{
    [...]
    
    public IEnumerable<AddressDTO> Endereco { get; set; }
}

When I do this, the error pops right into the first person who has the address filled in, and the error message is pretty much the same, but this time it speaks the opposite of the previous one: asks me to define my AddressDTO as a normal object, because the endereco returned from API is not a list.

I’ve tried to treat it right on the property Endereco of RecordDTO, expanding the { get; set; } to make some logic, but the error occurs soon in the get property. Therefore, I do not have access to the parameter value which brings the values of the object to be assigned by dotnet.

I don’t know what I can do in this situation. The last alternative would be to contact the API maintainers to show the case.

Thank you in advance if you have any solution.

  • "The problem occurs when returning the API when the address object changes, it changes according to whether the person has a registered address or not" an endpoint that returns multiple objects? this seems quite wrong, a contract must be respected and always return the same thing

  • Yeah, that’s what I think is weird. I can’t confirm that the objects are actually different because I don’t have access to the API code, but that’s what it looks like. Or the serializer is automatically inserting these brackets into the address when it comes null. This occurs both using Insomnia or Postman and debugging the C# project by consuming the API.

  • I believe that the correct thing would be for you to contact the developers of the API that you are consuming, because even if you manage to get around this problem, it may be that tomorrow or later they change this return because it is a mistake. Then you’d have to go back to fiddling with your code.

  • If you still want to continue, try manipulating JSON serialization to ignore empty array. using

  • but vc can make a request by Postman or Curl for example and confirm how the objects are. An endpoint can only return a type, otherwise it is a break change in relation to the contract, it may be the case that one scenario returns not part of the object (null) and another returns, if so would be ok, just take the contract of the most complete class... however, if someone provides an endpoint, need to have an example of the contract, and from there you can build your classes

  • @Marcosjunior opa, I had taken a look at the overloads of Deserializeobject but did not find an option for it. But I think if I do it right in the properties of my object, it might work, I’ll try. Thanks for the tip.

  • @Ricardopunctual then, I made requests by Postman and Insomnia, the return is the same, as I described in the question and more above in my reply to your first comment. The rest of the process is all ready and tested, just this detail that still gets me. API documentation is very vague.

Show 2 more comments

1 answer

0


The solution was to change the type of my object AddressDTO for dynamic, this way the deserialization happens without problems.

public class RecordDTO
{
    [...]
    
    public dynamic Endereco { get; set; }
}

When the return address arrived as endereco: [], he was serialized with the type JArray and when it was filled correctly, it was JObject. These are two Newtonsoft library types that are inside the namespace using Newtonsoft.Json.Linq;

With that, it was only to assemble the validations on top of these types.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.