2

Suppose I have a Refit interface:

public interface SomeApi
{
    [Get("/DoStuff")]
    Task<MyClass> DoStuff();
} 

I register this refit client in my dependency injection with System.Text.Json:

services.AddRefitClient<SomeApi>(new RefitSettings(
                                    new SystemTextJsonContentSerializer(
                                        new JsonSerializerOptions
                                            {
                                                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                                            })))
        .ConfigureHttpClient(ConfigureClient(config));

This code used to use Newtonsoft.Json, and has been refactored to use System.Text.Json. However,my /DoStuff endpoint can return null. With System.Text.Json, this will throw an exception in my Refit client. With Newtonsoft this works fine.

How do I get the above code to work if my api endpoints can return null?

yesman
  • 7,165
  • 15
  • 52
  • 117
  • Can you please share the full `ToString()` output of the exception, or maybe a [mcve]? If I just try to deserialize the string `null` to a dummy class `MyClass` with System.Text.Json, it deserializes successfully, see https://dotnetfiddle.net/kTvWpg. – dbc Apr 26 '22 at 15:00
  • Maybe your endpoint was actually returning **an empty string** instead of **`null`**? An empty string is [technically malformed JSON](https://stackoverflow.com/q/30621802/3744182), but Json.NET will interpret it as a null root value while System.Text.Json will throw an error. See https://dotnetfiddle.net/Mu6d0S – dbc Apr 26 '22 at 15:06
  • @dbc thanks for your time. I think you have a point in that my api may be returning an empty string instead. In the debugger I can see it’s a null value, but maybe asp.net’s internal magic translates that to an empty string before sending it to my refit client. If that was the case, how would I make System.Text.Json resistant to this? – yesman Apr 26 '22 at 16:18
  • I don't think you can. An empty string is malformed JSON, and System.Text.Json is designed to be a strict, unforgiving parser that rejects malformed JSON. `Utf8JsonReader`, being a struct, can't be subclassed and extended. Best would be to fix your endpoints to return well-formed JSON. – dbc Apr 26 '22 at 16:59
  • If you can't do that, you may need to create your own versions of [SystemTextJsonContentSerializer](https://github.com/reactiveui/refit/blob/main/Refit/SystemTextJsonContentSerializer.cs) and [`HttpContentJsonExtensions.ReadFromJsonAsyncCore()`](https://github.com/dotnet/runtime/blob/215b39abf947da7a40b0cb137eab4bceb24ad3e3/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpContentJsonExtensions.cs#L46) that preloads some of the content stream to check whether it is empty. – dbc Apr 26 '22 at 17:02
  • Annoyingly `BufferedStream` doesn't have a `TryPeek()` method, so you may need to load the first N bytes into a `MemoryStream`, check for an empty stream, and if not empty, create a concatenated stream from the memory stream and the content stream and pass it to `DeserializeAsync`. – dbc Apr 26 '22 at 17:04
  • To concatenate streams you could start with [How do I concatenate two System.IO.Stream instances into one?](https://stackoverflow.com/q/3879152/3744182) but those answers are all old and don't seem to handle async reading which is what you need here. Maybe this [`ConcatenatedStream`](https://gist.github.com/ramtinak/14454c854b3bcaabfc317c8595d11d1e) by [ramtinak](https://gist.github.com/ramtinak) would work? But honestly this all sounds like a pain. – dbc Apr 26 '22 at 17:05
  • Thanks for your help! I guess getting the api endpoints you return correct json at all times is the way forward then. – yesman Apr 26 '22 at 17:51

2 Answers2

2
new JsonSerializerOptions
    {
       PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
       DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
    }

documentation

J.Salas
  • 1,268
  • 1
  • 8
  • 15
0

Figured it out with the help of @dbc . Since System.Text.Json is very strict about with is valid json and what isn't, you have two options: make your API return a valid json (in this case, {} instead of null), or revert back to using Newtonsoft (which is less strict).

yesman
  • 7,165
  • 15
  • 52
  • 117