0

I have a json that I am deserializing by using NewtonSoftJson Json library as shown below:

public async Task InvokeAsync(HttpContext httpContext, ISchema schema)
{
    ...
    var request = Deserialize<GraphQLRequest>(httpContext.Request.Body);
    ....
}

public static T Deserialize<T>(Stream s)
{
    using (var reader = new StreamReader(s))
    using (var jsonReader = new JsonTextReader(reader))
    {
        var ser = new JsonSerializer();
        return ser.Deserialize<T>(jsonReader);
    }
}

Now I am trying to use System.Text.Json library to deserialize asynchronously so I read their docs and they do have DeserializeAsync method which I can use but when I replace content of above method like below then I get compilation error -

public static T Deserialize<T>(Stream s)
{
  return JsonSerializer.DeserializeAsync<GraphQLRequest>(s, new JsonSerializerOptions
      {
          PropertyNameCaseInsensitive = true
      }
  );
}

Error I get is -

Cannot convert expression type 'System.Threading.Tasks.ValueTask<App.Api.Kestrel.GraphQLRequest?>' to return type 'T'

I am new to dotnet world so kinda confuse what is wrong I am doing here?

AndyP
  • 527
  • 1
  • 14
  • 36

1 Answers1

3
  1. public static T Deserialize<T>(Stream s) is a generic method where T is type parameter which is passed by user, so you need to deserialize to T, not to GraphQLRequest.

  2. async methods usually (but not always) retrun Task's, so you need to change your signature and implementation accordingly resulting in:

public static async Task<T> Deserialize<T>(Stream s)
{
  return await JsonSerializer.DeserializeAsync<T>(s, new JsonSerializerOptions
      {
          PropertyNameCaseInsensitive = true
      }
  );
}

And then pass concrete type in the invocation (and use await):

public async Task InvokeAsync(HttpContext httpContext, ISchema schema)
{
    ...
    GraphQLRequest request = await Deserialize<GraphQLRequest>(httpContext.Request.Body);
    ....
}

Since JsonSerializer.DeserializeAsync returns a ValueTask another option is to pass it through without awaiting:

public static ValueTask<T> Deserialize<T>(Stream s)
{
  return JsonSerializer.DeserializeAsync<T>(s, new JsonSerializerOptions
      {
          PropertyNameCaseInsensitive = true
      }
  );
}

The calling code will not change.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Now it makes sense as I understood what I was missing. Also if my json fails to deserialize then `System.Text.Json` library will throw `System.Text.Json.JsonException` always or is there any case where it can throw some other exceptions for deserialization? I was looking at the docs and source code but couldn't see it clearly what it will throw so I wanted to make sure on that. But when I read `JsonException` class there it is clearly mentioned so just wanted to make sure – AndyP Sep 04 '21 at 20:27
  • 1
    @AndyP documentation for [`DeserializeAsync`](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer.deserializeasync?view=net-5.0) linked in answer lists exceptions thrown by the method, – Guru Stron Sep 05 '21 at 22:04
  • Note from [PropertyNameCaseInsensitive](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.propertynamecaseinsensitive): "There is a performance cost associated with case-insensitive comparison (that is, when PropertyNameCaseInsensitive is true)." – r23 Apr 30 '22 at 19:28