Search code examples
c#jsondynamicsystem.text.json

RuntimeBinder exception when using System.Text.Json.Serialize with dynamic type


public string PrettifyJson(string json)
{
    dynamic? parsedJson = JsonSerializer.Deserialize<dynamic>(json);
    var optionPrettyPrint = new JsonSerializerOptions { WriteIndented = true };
    string formattedJson = JsonSerializer.Serialize(parsedJson, optionPrettyPrint);
    
    return formattedJson;
}

Running the above, I get (only in Release, not in Debug!)

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The call is ambiguous between the following methods or properties: 'System.Text.Json.JsonSerializer.Serialize<System.Text.Json.JsonSerializerOptions>(System.IO.Stream, System.Text.Json.JsonSerializerOptions, System.Text.Json.JsonSerializerOptions)' and 'System.Text.Json.JsonSerializer.Serialize<System.Text.Json.JsonSerializerOptions>(System.Text.Json.Utf8JsonWriter, System.Text.Json.JsonSerializerOptions, System.Text.Json.JsonSerializerOptions)' at CallSite.Target(Closure, CallSite, Type, Object, JsonSerializerOptions) at System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2) at Test.JsonUtil.PrettifyJson(String json) in D:\gitlab-runner\builds\RVQYCTWy\0\jpsc\developer\api-test\JsonUtil.cs:line 22

It seems there is a problem to find the correct Serialize() overload, since the first parameter is dynamic, but I don't really want to have to put a generic type on PrettifyJson, and it works as expected in Debug mode.

Any hints?


Solution

  • I can reproduce the problem: just pass in "null" as the value for json. At that point, parsedJson is a null reference, and there are multiple overloads that are applicable.

    The fix is to just avoid using dynamic typing, which isn't actually buying you anything here as far as I can see - just use object instead:

    public string PrettifyJson(string json)
    {
        object? parsedJson = JsonSerializer.Deserialize<object>(json);
        var optionPrettyPrint = new JsonSerializerOptions { WriteIndented = true };
        string formattedJson = JsonSerializer.Serialize(parsedJson, optionPrettyPrint);
        
        return formattedJson;
    }
    

    Then when "null" is parsed in, you still end up calling JsonSerializer.Serialize<object>(null, optionPrettyPrint) which returns "null" back.