0

I am working on .NET 5 API application xUnit Tests. I am receiving JSON object in the following format. In the JSON object, I have an nested object with the title 'data'. I need to deserialize data object into class. The data object is of type Dynamic. The whole object can deserialize to ResponseResult class which I don't want, I want to pick only object with title 'data' and deserialize to specific class

error

var actualResultData = JsonConvert.DeserializeObject<ResponseResult>(actualResult.Content).Data;
var dynamicDataObject = JsonConvert.DeserializeObject<HandHeldDeviceDataView>(actualResultData);

 The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject<HandHeld.Domain.DTOs.DataView.HandHeldDeviceDataView>(string)' has some invalid arguments

enter image description here

Test Method

   [Fact]
    public async Task GetHandHeldByIMEI_ShouldReturn_DataTypeOf_HandHeldWrapperDataView()
    {
        //Arrange
        var fixture = new Fixture();
        var imei = "imeiNo";
        var handHeldWrapperMoq = fixture.Create<HandHeldWrapperDataView>();
        var returnResultMoq = ResponseResultHelper.SuccessfulResult(handHeldWrapperMoq.HandHeld, handHeldWrapperMoq.ResultSummary);
        var returnSerializeObjectMoq = JsonConvert.SerializeObject(returnResultMoq, Formatting.None);
        

        mediatorMoq.Setup(x => x.Send(It.IsAny<GetHandHeldByIMEI>(), It.IsAny<CancellationToken>())).ReturnsAsync(handHeldWrapperMoq);

        //Act
        var actualResult = await sut.GetHandHeldByIMEI(imei) as ContentResult;
        var actualResultData = JsonConvert.DeserializeObject<ResponseResult>(actualResult.Content).Data; // this is not working
       
        //Assert
        //Assert.Equal(returnSerializeObjectMoq, actualResult.Content);
       // Assert.IsAssignableFrom<HandHeldDeviceDataView>(actualResultData);
    }

JSON Object

{
"Ok":true,
"RecordCount":118,
"Error":null,
"InnerExceptionMessage":null,
"ExecutionMessage":"ExecutionMessage40f96da6-ae4d-41a4-b98d-5e49021959d7",
"Data":{
   "HandHeldId":68,
   "ParkingAttendantId":92,
   "IMEI":"IMEI220d7976-f806-4ba7-ad06-e79bc6227f64",
   "HandHeldAppId":"HandHeldAppId7cc85661-4d7d-4d78-a7d8-f183de88b627",
   "SerialNumber":"SerialNumber063573ec-bcf6-408b-934f-0834d8824bca",
   "PhoneNumber":"PhoneNumber639f894e-4dd7-4e24-899d-a28f031331d6",
   "PrinterSerialNo":"PrinterSerialNoaf0d0082-5b21-476f-8cb8-c6c9f53d81a3",
   "LastSeenDateTime":"2019-10-08T20:43:05.829758+01:00",
   "LastPrintDateTime":"2022-11-01T05:39:58.3454711+00:00",
   "IsActive":true,
   "IsDeleted":false,
   "CreatedBy":87,
   "UpdatedBy":16,
   "CreatedDateTimeUtc":"2022-05-30T04:22:53.2192713+01:00",
   "UpdatedDateTimeUtc":"2019-12-16T03:06:14.3078736+00:00",
   "testField":247
 }
}

ResponseResult Class

in this class I have property 'data' dynamic which I want to deserialize to specific class

public class ResponseResult
{
    public bool Ok { get; set; }
    public int RecordCount { get; set; }
    public string Error { get; set; }
    public string InnerExceptionMessage { get; set; }
    public string ExecutionMessage { get; set; }
    public dynamic Data { get; set; }
}

and I have found the answer;

 var actualResultData = JsonConvert.DeserializeObject<ResponseResult>(actualResult.Content).Data.ToObject<HandHeldDeviceDataView>();
K.Z
  • 5,201
  • 25
  • 104
  • 240
  • Did you received any exception or error ? – Mihai Alexandru-Ionut Jul 12 '21 at 07:40
  • 2
    What do you mean by `this is not working` ? – Chetan Jul 12 '21 at 07:41
  • It may be worth noting (in your question body) that you are using `Newtonsoft.Json`, not `System.Text.Json`, I only spotted that while reading your code and seeing `JsonConvert` and not `JsonSerializer` – MindSwipe Jul 12 '21 at 07:47
  • so far this is how I manage to deserialize nested object. 1- deserialize the whole object ResponseResult and get data object. 2 - serialize data object again 3- deserialize data object to HandHeldDeviceDataView class which eventually I want it... – K.Z Jul 12 '21 at 07:48
  • Please add a exception to your post, so we can see where your code stucks. Maybe you should with the JObject and JToken class? – Robert Wolf Jul 12 '21 at 07:50
  • The way I'd go about is to make `ResponseResult` generic, and then use `public T Data { get; set; }` and implement different types of it, like `ImeiResponseResult : ResponseResult` and then deserialize to that, like `var imei = JsonConvert.DeserializeObject(actualResult.Content).Data;` – MindSwipe Jul 12 '21 at 07:52
  • the error is The best overloaded method match for 'Newtonsoft.Json.JsonConvert.DeserializeObject(string)' has some invalid arguments – K.Z Jul 12 '21 at 07:53
  • You could try to create your "Data" class and build the json schema from it. Than you could check if the Data content is valid against the schema and if so, deserialize from it. – Robert Wolf Jul 12 '21 at 07:57
  • I have tried Robert, please refer in error session in my question – K.Z Jul 12 '21 at 07:58

1 Answers1

2

I highly recommend avoiding the use of dynamic at all costs, C# is a strongly typed language and has all the advantages of one, don't throw them away. Instead, I'd recommend you change your ResponseResult class to a generic and then use different classes to deserialize to, like so:

public class ResponseResult<T>
{
    // ...

    public T Data { get; set; }
}

public class HandHeldResult
{
    public int HandHeldId { get; set; }

    // ...
}

You would now deserialize to ResponseResult<HandHeldResult>, like so:

var actualResult = await sut.GetHandHeldByIMEI(imei) as ContentResult;
var data= JsonConvert.DeserializeObject<ResponseResult<HandHeldResult>>(actualResult.Content).Data;

This way you keep the type information and can catch errors at compile time. See this dotnetfiddle for a working demo

MindSwipe
  • 7,193
  • 24
  • 47
  • I have thought of this approach when designing my APIs but that means I need to create class for every single type return. with above approach I run test and it did work, thanks for that. – K.Z Jul 12 '21 at 09:15
  • I have found another solution where I used ToObject() to convert already deserialize object that in my case 'Data Object' ; to Specific Class Type – K.Z Jul 12 '21 at 09:20
  • @Toxic you could deserialize to `ResponseResult` directly, it just wasn't clear what type you wanted `Data` to be, so I just made one up as a demonstration, but this works for every class. Even if `Data` is just a string you can easily do `JsonConvert.DeserializeObject>(...)` – MindSwipe Jul 12 '21 at 11:12