Aspnetcore: Blazor: IJSRuntime.InvokeAsync serialization broken for arrays of objects

Created on 22 Mar 2019  路  2Comments  路  Source: dotnet/aspnetcore

I have a simple JavaScript helper:

window.debugOut = (content) => {
  console.dir(content);
}

On the Blazor side I have this type:

public class City
{
  public string CityName { get; set; }
  public int Population { get; set; }
}

If I call the JavaScript debugOut and pass a List<City>, this arrives correctly on the browser side:

await Js.InvokeAsync<bool>("debugOut", new List<City> {
  new City{CityName="London",Population=8000000 },
  new City{CityName="Edinburgh", Population=400000 }
});

image

But if I use an array type instead, the browser only sees the first object:

await Js.InvokeAsync<bool>("debugOut", new City[] {
  new City{CityName="London",Population=8000000 },
  new City{CityName="Edinburgh", Population=400000 }
});

image

Serialization works correctly with an array of simple values. The problem seems to occur only with arrays of objects.

await Js.InvokeAsync<bool>("debugOut", new int[] { 1, 2, 3 });

image

area-blazor

Most helpful comment

This is because InvokeAsync accepts params object[] args, and since you're passing an array, the compiler treats it as if you want each array entry to be a separate top-level entry in args. What you want instead is for args to be an array containing only one top-level entry, which itself is your array.

This is the nature of params in C# in general. For more info, see https://stackoverflow.com/questions/36350/how-to-pass-a-single-object-to-a-params-object

To override what the compiler does here, consider casting to object, e.g.:

await Js.InvokeAsync<bool>("debugOut", (object) new City[] {
  new City{CityName="London",Population=8000000 },
  new City{CityName="Edinburgh", Population=400000 }
});

This will have no runtime cost and will give the parameter filling behavior you want.

All 2 comments

This is because InvokeAsync accepts params object[] args, and since you're passing an array, the compiler treats it as if you want each array entry to be a separate top-level entry in args. What you want instead is for args to be an array containing only one top-level entry, which itself is your array.

This is the nature of params in C# in general. For more info, see https://stackoverflow.com/questions/36350/how-to-pass-a-single-object-to-a-params-object

To override what the compiler does here, consider casting to object, e.g.:

await Js.InvokeAsync<bool>("debugOut", (object) new City[] {
  new City{CityName="London",Population=8000000 },
  new City{CityName="Edinburgh", Population=400000 }
});

This will have no runtime cost and will give the parameter filling behavior you want.

Nice one, I didn't think of that. I guess I don't work with arrays much in real life :)

Was this page helpful?
0 / 5 - 0 ratings