question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Customize serialization for IJSRuntime.InvokeAsync

See original GitHub issue

Before I write anything else… I have already created an issue a while ago with pretty much the same problem (which I will copy to here as well) but with a solution that didn’t really make sense. I now have a concrete idea/solution which is a lot better than what I suggested in my previous request.

Issue (similar to old issue)

Because IJSRuntime.InvokeAsync now uses System.Text.Json instead of Newtonsoft.Json (preview6+) the custom serialization I implemented with Newtonsoft.Json doesn’t work anymore. Specifically null values aren’t ignored anymore.
Even though it says in the issue I linked…

Users may use a JSON serializer of their choice.

… the IJSRuntime doesn’t use Newtonsoft.Json if you add Newtonsoft.Json in the ConfigureServices method using AddNewtonsoftJson on the IMvcBuilder.

I have already made a comment in the discussion for this issue describing the issue and what I already tried (back then I didn’t know this wasn’t the default behaviour). In this comment I also link to my SO question where you can find further information on the attempts I’ve made (same as before, I assumed this wasn’t default behaviour).

These issues and questions might mention the need to be able to use Newtonsoft.Json with IJSRuntime. This is not anymore the case as I want to migrate all the custom serialization to System.Text.Json anyway as soon as the API allows all the desired customizations I require.

Request / Idea

Now instead of allowing users to customize what serializer is used (replace System.Text.Json.JsonSerializer), I only want to talk about the JsonSerializerOptions (customize System.Text.Json.JsonSerializer).
I have examined the source code for IJSRuntime and the corresponding implementation(s). From that I have found out that the base class of all implementations (JSRuntimeBase) uses the class JsonSerializerOptionsProvider (see Line 67) which always holds the same JsonSerializerOptions. This can’t be influenced by a user of the API.

Now how would you allow the user to customize those JsonSerializerOptions?
It seems like it would be as easy adding another optional parameter to the InvokeAsync methods of type JsonSerializerOptions with a default value of null. For documentation purposes it might be a good idea to make JsonSerializerOptionsProvider public instead of internal so you could say something along the lines of:

if not provided <see cref="JsonSerializerOptionsProvider.DefaultOptions"> will be used.

This seems to already be quite detailed and is of course not up to me to decide, I just wanted to bring up the idea.

I realize that my old request was a bit too broad as well as probably too expensive to implement currently. This however seems much more lightweight and with a clear solution. I think it would be a great addition to this API as it can be a great advantage to be able to customize how the IJSRuntime serializes your arguments.
The biggest advantage for me (and probably one of the biggest use cases overall) would be that I could set IgnoreNullValues to true which is of course not the default value.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:15
  • Comments:59 (8 by maintainers)

github_iconTop GitHub Comments

20reactions
rChavzcommented, Mar 24, 2020

some temporally solution

public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);

            ConfigureServices(builder.Services);

            builder.RootComponents.Add<App>("app");

            var host = builder.Build();

            ConfigureProviders(host.Services);

            await host.RunAsync();
        }

        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();
        }

        public static void ConfigureProviders(IServiceProvider services)
        {
            try
            {
                var jsRuntime = services.GetService<IJSRuntime>();
                var prop = typeof(JSRuntime).GetProperty("JsonSerializerOptions", BindingFlags.NonPublic | BindingFlags.Instance);
                JsonSerializerOptions value = (JsonSerializerOptions)Convert.ChangeType(prop.GetValue(jsRuntime, null), typeof(JsonSerializerOptions));
                value.PropertyNamingPolicy = null;
                value.IgnoreNullValues = true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SOME ERROR: {ex}");
            }
        }
    }
12reactions
Carl-Hugocommented, Mar 10, 2020

I wanted to give another shot at Blazor, and I encountered a similar issue when interfacing with a third-party JavaScript library. They were not handling null properties but were handling missing properties.

I would love to have access to the JsonSerializerOptions instance used by the JSRuntime or an attribute that would allow me to decorate my types/properties, telling them to [IgnoreNullOrWhateverTheNameThatYouChoose].

I spent a few hours digging into your code to find out that despite all of that focus on dependency injection, you decided to new the JsonSerializerOptions in the JSRuntime constructor and assigned it to an inaccessible property of that abstract class. That class have one or more concretions, but they are all internal, so inaccessible to us mortals (heuuuu… I meant simple application developers).

Well, even if WebAssemblyJSRuntime was accessible, converting/casting the IJSRuntime to JSRuntime (or worst to WebAssemblyJSRuntime) would not be a super-strong solution. WebAssemblyJSRuntime is sealed so I could not inherit from it to update the options, which we should not have to do anyway; another not so strong/impossible solution…

image

To fix my problem, I just sent untyped anonymous objects to JavaScript, but that’s not a solution for a real project (mine is a small experiment, so I don’t mind losing the types). It would have been so much easier to just be able to configure the JsonSerializerOptions and set the IgnoreNullValues to true. It would also follow your own patterns about how to configure options, making it easier for everyone (i.e. being linear throughout the .Net Core ecosystem).

That said, I think that you should focus on better interoperability with JavaScript. There are countless libraries already implemented, and it would be a shame not to be able to use them when needed. Yes, at some point, more and more Blazor libraries will appear, but I believe that it would help kickstart Blazor if we could just use JS stuff easily…

Keep up the good work on making .Net a better place!

Read more comments on GitHub >

github_iconTop Results From Across the Web

IJSRuntime ignores custom json serializer in server side ...
I first serialized my object with json.net so I could define NullValueHandling = NullValueHandling.Ignore and so all the custom converters got ...
Read more >
IJSRuntime.InvokeAsync Method (Microsoft.JSInterop)
Invokes the specified JavaScript function asynchronously. JSRuntime will apply timeouts to this operation based on the value configured in ...
Read more >
Call JavaScript functions from .NET methods in ASP. ...
To call into JS from .NET, inject the IJSRuntime abstraction and call one of the following methods: IJSRuntime.InvokeAsync · JSRuntimeExtensions ...
Read more >
Calling JavaScript from .NET
@page "/" @inject IJSRuntime JSRuntime <button ... Parameters are serialized to JSON and then deserialized in JavaScript before being passed by-value as an ......
Read more >
Blazor FAQ - JavaScript Interop
Create an export JavaScript function in the wwwroot/script folder. ... Import the JavaScript function using the IJSRuntime.InvokeAsync method in Blazor and call ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found