aspnetcore
aspnetcore copied to clipboard
A published blazor wasm app throws a serialization error "Unhandled exception rendering component: ConstructorContainsNullParameterNames"
Is there an existing issue for this?
- [X] I have searched the existing issues
Describe the bug
An error is thrown when making a JS interop call in a published blazor wasm app that should serialize a KeyValuePair
value.
This seems to be related to trimming as running this with things like dotnet run --configuration Release
works as expected, but publishing the app starts the trimmer by default.
In addition, using an anonymous type instead of KeyValuePair
works without any errors.
[!NOTE] I tried to disable the trimming, but the error is still present. Any suggestions on how to do that will be helpful - this might turn out to be another issue though.
Expected Behavior
No error is thrown.
Steps To Reproduce
- Clone https://github.com/Stamo-Gochev/blazor-issue-wasm-serialization
- Change directory to https://github.com/Stamo-Gochev/blazor-issue-wasm-serialization/tree/master/BlazorissueWasmSerialization/BlazorissueWasmSerialization
- Run
dotnet publish --configuration Release
- Go to
BlazorissueWasmSerialization/BlazorissueWasmSerialization/bin/Release/net8.0/publish
- Run
dotnet BlazorissueWasmSerialization.dll
- Click the "Show issue button"
Exceptions (if any)
Exception
blazor.web.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String] SerializationNotSupportedParentType, System.Object Path: $.
System.NotSupportedException: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String] SerializationNotSupportedParentType, System.Object Path: $.
---> System.NotSupportedException: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String]
at System.Text.Json.ThrowHelper.ThrowNotSupportedException_ConstructorContainsNullParameterNames(Type )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.PopulateParameterInfoValues(JsonTypeInfo )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateTypeInfoCore(Type , JsonConverter , JsonSerializerOptions )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateJsonTypeInfo(Type , JsonSerializerOptions )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type , JsonSerializerOptions )
at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type )
at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, CachingContext context)
--- End of stack trace from previous location ---
at System.Text.Json.JsonSerializerOptions.CachingContext.CacheEntry.GetResult()
at System.Text.Json.JsonSerializerOptions.CachingContext.GetOrAddTypeInfo(Type , Boolean )
at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type , Boolean , Nullable`1 , Boolean , Boolean )
at System.Text.Json.Serialization.Metadata.JsonTypeInfo.Configure()
at System.Text.Json.Serialization.Metadata.JsonTypeInfo.<EnsureConfigured>g__ConfigureSynchronized|172_0()
at System.Text.Json.Serialization.Metadata.JsonTypeInfo.EnsureConfigured()
at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type , Boolean , Nullable`1 , Boolean , Boolean )
at System.Text.Json.WriteStackFrame.InitializePolymorphicReEntry(Type , JsonSerializerOptions )
at System.Text.Json.Serialization.JsonConverter.ResolvePolymorphicConverter(Object , JsonTypeInfo , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Object& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.Converters.DictionaryOfTKeyTValueConverter`3[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnWriteResume(Utf8JsonWriter , Dictionary`2 , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonDictionaryConverter`3[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnTryWrite(Utf8JsonWriter , Dictionary`2 , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Dictionary`2& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWriteAsObject(Utf8JsonWriter , Object , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Object& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.Converters.ArrayConverter`2[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnWriteResume(Utf8JsonWriter , Object[] , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonCollectionConverter`2[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnTryWrite(Utf8JsonWriter , Object[] , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Object[]& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].WriteCore(Utf8JsonWriter , Object[]& , JsonSerializerOptions , WriteStack& )
Exception_EndOfInnerExceptionStack
at System.Text.Json.ThrowHelper.ThrowNotSupportedException(WriteStack& , NotSupportedException )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].WriteCore(Utf8JsonWriter , Object[]& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Serialize(Utf8JsonWriter , Object[]& , Object )
at System.Text.Json.JsonSerializer.WriteString[Object[]](Object[]& , JsonTypeInfo`1 )
at System.Text.Json.JsonSerializer.Serialize[Object[]](Object[] , JsonSerializerOptions )
at Microsoft.JSInterop.JSRuntime.InvokeAsync[IJSVoidResult](Int64 , String , CancellationToken , Object[] )
at Microsoft.JSInterop.JSRuntime.<InvokeAsync>d__16`1[[Microsoft.JSInterop.Infrastructure.IJSVoidResult, Microsoft.JSInterop, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
at Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(IJSRuntime , String , Object[] )
at BlazorissueWasmSerialization.Client.Pages.Home.OnButtonShowIssueClick()
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task , ComponentState )
.NET Version
8.0.100
Anything else?
No response
I'm getting the identical exception, also in a published WASM app serializing a KeyValue pair. Error does not happen locally. In my case this is the Telerik blazor grid serializing or deserializing a KeyValue pair under the hood that is related to a default date filter in the grid. As a result this error appears on any grid that has a filterable date column.
@Stamo-Gochev has this ever worked in a previous version of Blazor?
What I suspect is happening is that the KeyValuePair is being linked out. That's because the constructor and other elements are not being preserved. It works with an anonymous type because that type is created within the app assembly and Blazor doesn't serialize that.
If the members are used elsewhere in the app, the issue will go away, but seems that they are only being used during serialization. You can use https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options?pivots=dotnet-8-0#root-descriptors and https://github.com/dotnet/runtime/blob/main/docs/tools/illink/data-formats.md#descriptor-format to teach the linker not to strip away members from the type.
If you are still running into issues after taking those steps, let us know, it might be a different linker issue.
@javiercn I have an application that began exhibiting this problem upon upgrading to .NET 8. I didn’t do too much research as I figured you all would hash this bug out, but if you’re under the impression this is not a new issue/not planning to be addressed soon I can also do more digging.
Hi @Stamo-Gochev. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
@taylorchasewhite If the issue happens at the linking stage, there are two possibilities:
- The type is not being preserved; this requires an individual case analysis to determine whether it's by design or whether there is an issue with any of our annotations.
- The type is being correctly told to be preserved, but despite that, there are some issues. In that case, it can be a bug in the linker, but with the info collected we can point that out to the linker folks and they can further investigate.
In most situations, the member is simply not being preserved, and that matters most when you are using types from the framework, since gestures that you might expect will result in them being preserved (like being passed to a JS interop call) won't actually do so.
In those situations, the descriptor file is the way to go.
has this ever worked in a previous version of Blazor?
Yes it has, I tested it on the same exact project just with different targets, on .NET 7 it works fine, but on .NET 8 an exception occurs ☹️
https://github.com/dotnet/aspnetcore/assets/47293197/278e079c-90b4-4550-b614-1513e8118e21
here's the project repo: https://github.com/SaifAqqad/blazor-bug-test
Imo even if this is the intended behavior of the trimmer, there should be a built-in exclusion of certain types (serialization, etc) for wasm projects
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.
See our Issue Management Policies for more information.
@javiercn
@Stamo-Gochev has this ever worked in a previous version of Blazor?
What I suspect is happening is that the KeyValuePair is being linked out. That's because the constructor and other elements are not being preserved. It works with an anonymous type because that type is created within the app assembly and Blazor doesn't serialize that.
If the members are used elsewhere in the app, the issue will go away, but seems that they are only being used during serialization. You can use https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options?pivots=dotnet-8-0#root-descriptors and https://github.com/dotnet/runtime/blob/main/docs/tools/illink/data-formats.md#descriptor-format to teach the linker not to strip away members from the type.
If you are still running into issues after taking those steps, let us know, it might be a different linker issue.
Yes, it works with previous versions - .NET 6.0 and .NET 7.0.
The Home.razor page and the JS file can be pasted in a standalone .NET 6.0 and .NET 7.0 projects for comparison.
Regarding the suggestions:
If the members are used elsewhere in the app, the issue will go away, but seems that they are only being used during serialization. You can use https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options?pivots=dotnet-8-0#root-descriptors and https://github.com/dotnet/runtime/blob/main/docs/tools/illink/data-formats.md#descriptor-format to teach the linker not to strip away members from the type.
this is possible, but the change in the behavior looks more or less like a regression to me - it is not clear why KeyValuePair
was serializeable before and not now.
As .NET 8.0 is a major release, I can accept a public issue that states that KeyValuePair
is no longer serializable when linking is enabled, but I couldn't find such an issue, which is why it seems like a regression to me.
This seems to be a regression in the linker in fact.
Tagging subscribers to this area: @dotnet/area-system-text-json, @gregsdennis See info in area-owners.md if you want to be subscribed.
Issue Details
Is there an existing issue for this?
- [X] I have searched the existing issues
Describe the bug
An error is thrown when making a JS interop call in a published blazor wasm app that should serialize a KeyValuePair
value.
This seems to be related to trimming as running this with things like dotnet run --configuration Release
works as expected, but publishing the app starts the trimmer by default.
In addition, using an anonymous type instead of KeyValuePair
works without any errors.
[!NOTE] I tried to disable the trimming, but the error is still present. Any suggestions on how to do that will be helpful - this might turn out to be another issue though.
Expected Behavior
No error is thrown.
Steps To Reproduce
- Clone https://github.com/Stamo-Gochev/blazor-issue-wasm-serialization
- Change directory to https://github.com/Stamo-Gochev/blazor-issue-wasm-serialization/tree/master/BlazorissueWasmSerialization/BlazorissueWasmSerialization
- Run
dotnet publish --configuration Release
- Go to
BlazorissueWasmSerialization/BlazorissueWasmSerialization/bin/Release/net8.0/publish
- Run
dotnet BlazorissueWasmSerialization.dll
- Click the "Show issue button"
Exceptions (if any)
Exception
blazor.web.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String] SerializationNotSupportedParentType, System.Object Path: $.
System.NotSupportedException: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String] SerializationNotSupportedParentType, System.Object Path: $.
---> System.NotSupportedException: ConstructorContainsNullParameterNames, System.Collections.Generic.KeyValuePair`2[System.String,System.String]
at System.Text.Json.ThrowHelper.ThrowNotSupportedException_ConstructorContainsNullParameterNames(Type )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.PopulateParameterInfoValues(JsonTypeInfo )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateTypeInfoCore(Type , JsonConverter , JsonSerializerOptions )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.CreateJsonTypeInfo(Type , JsonSerializerOptions )
at System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver.GetTypeInfo(Type , JsonSerializerOptions )
at System.Text.Json.JsonSerializerOptions.GetTypeInfoNoCaching(Type )
at System.Text.Json.JsonSerializerOptions.CachingContext.CreateCacheEntry(Type type, CachingContext context)
--- End of stack trace from previous location ---
at System.Text.Json.JsonSerializerOptions.CachingContext.CacheEntry.GetResult()
at System.Text.Json.JsonSerializerOptions.CachingContext.GetOrAddTypeInfo(Type , Boolean )
at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type , Boolean , Nullable`1 , Boolean , Boolean )
at System.Text.Json.Serialization.Metadata.JsonTypeInfo.Configure()
at System.Text.Json.Serialization.Metadata.JsonTypeInfo.<EnsureConfigured>g__ConfigureSynchronized|172_0()
at System.Text.Json.Serialization.Metadata.JsonTypeInfo.EnsureConfigured()
at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type , Boolean , Nullable`1 , Boolean , Boolean )
at System.Text.Json.WriteStackFrame.InitializePolymorphicReEntry(Type , JsonSerializerOptions )
at System.Text.Json.Serialization.JsonConverter.ResolvePolymorphicConverter(Object , JsonTypeInfo , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Object& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.Converters.DictionaryOfTKeyTValueConverter`3[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnWriteResume(Utf8JsonWriter , Dictionary`2 , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonDictionaryConverter`3[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnTryWrite(Utf8JsonWriter , Dictionary`2 , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Dictionary`2& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWriteAsObject(Utf8JsonWriter , Object , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Object& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.Converters.ArrayConverter`2[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnWriteResume(Utf8JsonWriter , Object[] , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonCollectionConverter`2[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnTryWrite(Utf8JsonWriter , Object[] , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].TryWrite(Utf8JsonWriter , Object[]& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].WriteCore(Utf8JsonWriter , Object[]& , JsonSerializerOptions , WriteStack& )
Exception_EndOfInnerExceptionStack
at System.Text.Json.ThrowHelper.ThrowNotSupportedException(WriteStack& , NotSupportedException )
at System.Text.Json.Serialization.JsonConverter`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].WriteCore(Utf8JsonWriter , Object[]& , JsonSerializerOptions , WriteStack& )
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1[[System.Object[], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Serialize(Utf8JsonWriter , Object[]& , Object )
at System.Text.Json.JsonSerializer.WriteString[Object[]](Object[]& , JsonTypeInfo`1 )
at System.Text.Json.JsonSerializer.Serialize[Object[]](Object[] , JsonSerializerOptions )
at Microsoft.JSInterop.JSRuntime.InvokeAsync[IJSVoidResult](Int64 , String , CancellationToken , Object[] )
at Microsoft.JSInterop.JSRuntime.<InvokeAsync>d__16`1[[Microsoft.JSInterop.Infrastructure.IJSVoidResult, Microsoft.JSInterop, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
at Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(IJSRuntime , String , Object[] )
at BlazorissueWasmSerialization.Client.Pages.Home.OnButtonShowIssueClick()
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task , ComponentState )
.NET Version
8.0.100
Anything else?
No response
Author: | Stamo-Gochev |
---|---|
Assignees: | javiercn |
Labels: |
|
Milestone: | - |
Duplicate of dotnet/runtime#74141, https://github.com/dotnet/runtime/issues/94806 and https://github.com/dotnet/runtime/issues/81709.
The reason this error shows up on serialization traces as well as deserialization is because of this breaking change. I would recommend either switching to the source generator or applying a DynamicDependency
attribute on the affected types (see https://github.com/passwordless-lib/fido2-net-lib/commit/1fbfb25cb9f53a8795ab952698dcc6493df1add3 for an example).
The reason this error shows up on serialization traces as well as deserialization is because of this breaking change.
Does this mean that system.text.json doesn't support de/serializing KeyValuePair
? Since this is what this issue is about.
I still think this should be looked into more, especially for built-in types like KeyValuePair.
I would recommend either switching to the source generator or applying a
DynamicDependency
attribute on the affected types
How is it acceptable that serializing such simple types would need this special handling, it sounds like a nightmare tbh.
Throwing attributes around as a fix for such things is not acceptable imo.
The reason this error shows up on serialization traces as well as deserialization is because of this breaking change.
Does this mean that system.text.json doesn't support de/serializing
KeyValuePair
? Since this is what this issue is about.
No. It means that using reflection-based serialization in trimmed applications is not reliable and can result in multiple failures like the one reported here. This is a problem inherent to using dynamic member access via reflection in trimmed applications and there's not much that can be done about it other than adding manual annotations to work around the issues that pop up. Our recommendation is to switch to the source generator if you must use a trimmed app, see this article for more details.
@eiriktsarpalis I have same problem after upgrade to .Net 8. As you explained, we need to handle this problem by changing our code, and .Net will not support anymore like previous .Net 7 version?
@SaifAqqad @Stamo-Gochev any work around for this matter? I have same problem! Thanks!
@hoangdovan The fundamental issue is that trimming and reflection are fundamentally incompatible. There's nothing that we can do about this in the System.Text.Json layer other than recommend switching to the source generator which is trimming-safe.
@SaifAqqad @Stamo-Gochev any work around for this matter? I have same problem! Thanks!
It still requires changing your code though and whether you need to serialize a KeyValuePair
in several places or if you can replace it with another type.
One option is to create a custom serializer, another one is to map it to something like:
someKeyValuePair.Select(x => new { key = x.Key, value = x.Value })...
which follows the structure of a JSON-serialized KeyValuePair
.
@Stamo-Gochev Thank you. In my case, I create a custom class replace for .Net KeyPairValue to fix this error!
I tried to disable the trimming, but the error is still present. Any suggestions on how to do that will be helpful - this might turn out to be another issue though.
@eiriktsarpalis Others are experiencing this issue at blazor wasm throwing deserialization exception when published.
This seems to be a regression in the linker in fact.
@javiercn Can this issue be reopened? Multiple people are getting this exception when trimming is disabled.
<PropertyGroup>
<PublishTrimmed>false</PublishTrimmed>
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
</PropertyGroup>
Multiple people are getting this exception when trimming is disabled.
That is very surprising. A minimal reproduction (including the project configuration) would help here. I would recommend filing a new issue once the repro is available.
It's straightforward to repro. Details below performed using SDK 8.0.100 on Windows 11:
mkdir NullParameterNames
cd NullParameterNames
dotnet new blazor --interactivity WebAssembly
Replace contents of Counter.razor
in client with the following:
@page "/counter"
@rendermode @(new InteractiveWebAssemblyRenderMode(false))
@using System.Text.Json
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<dl>
@foreach (var item in @items!)
{
<dt>@item.Key</dt>
<dd>@item.Value</dd>
}
</dl>
@code {
private List<KeyValuePair<string, string>>? items;
protected override void OnInitialized()
{
string data = "[ { \"key\" : \"key 1\", \"value\" : \"value 1\" }, { \"key\" : \"key 2\", \"value\" : \"value 2\" } ]";
JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true };
items = JsonSerializer.Deserialize<List<KeyValuePair<string, string>>>(data, options)!;
}
}
If you run server and visit counter page it will work as expected:
cd NullParameterNames
dotnet run --configuration Release
Afterwards publish the app and run the exe:
dotnet publish --configuration Release -p:PublishTrimmed=false
cd bin\Release\net8.0\publish
NullParameterNames.exe
On visiting counter page you will get System.NotSupportedException
as detailed by OP.
Afterwards publish the app and run the exe:
Why would disabling trimming on the server app have any bearing on what happens to the client project?
I also notice that the reproduction is using reflection-based serialization APIs directly which to my knowledge isn't well supported in wasm. I'm actually surprised that this doesn't give out any warnings. Have you tried using the source generator instead? This would involve including a declaration as follows:
[JsonSerializable(typeof(List<KeyValuePair<string, string>>)]
partial class MyContext : JsonSerializerContext {}
and passing that into the serializer APIs.
From the STJ perspective this is by-design behavior per https://github.com/dotnet/aspnetcore/issues/52947. The constructor parameters for KeyValuePair have been trimmed away so we can't build a serialization contract for the type. There's nothing the serializer can really do about that.
I can try re-transfering the issue back to Blazor in case they can provide recommendations that are more specific than what we can provide.
Why would disabling trimming on the server app have any bearing on what happens to the client project?
I'm assuming that since the client csproj is referenced in the server csproj MSBuild
includes that in the build and uses the PublishTrimmed=false
option for both. Regardless the wwwroot\_framework
folder has 604 files (34.6 MB) with that option specified and 109 files (12.8 MB) without it (so does impact client build).
I also notice that the reproduction is using reflection-based serialization APIs directly which to my knowledge isn't well supported in wasm.
Well it worked in .NET 6/7 and apparently .NET 8 (in debug mode). I'm aware of the source generator for System.Text.Json
but reflection-based serialization is still useful for migrating projects from .NET 6/7 (as alluded to by earlier comments).
The constructor parameters for KeyValuePair have been trimmed away so we can't build a serialization contract for the type.
Understood. The central issue here is that the PublishTrimmed
option doesn't behave the same way in .NET 8 so transferring the issue from runtime
to aspnetcore
seems appropriate. Maybe also add area-Tools-ILLink
label so the linker/trimming team can take a look?
@eiriktsarpalis blazor doesn't offer the ability to configure the underlying serialization for S.T.J for JS interop.
If the type can't be annotated, I assume a descriptor to preserve the type is the only way to keep this code working?
I also notice that the reproduction is using reflection-based serialization APIs directly which to my knowledge isn't well supported in wasm.
Well it worked in .NET 6/7
That's due to the intentional breaking change discussed here: https://github.com/dotnet/aspnetcore/issues/52947#issuecomment-1866491518. TL;DR in older versions the repro in question would successfully serialize but still fail to deserialize with a similar error message.
If the type can't be annotated, I assume a descriptor to preserve the type is the only way to keep this code working?
Correct. Another thing to investigate is why the razor file in the repro doesn't emit trimmer warnings even though it is calling trim-unsafe APIs. We want to be emphatic with customers that they cannot reliably depend on reflection in trimmed applications and they should be moving to using the source generator instead -- emitting the relevant warnings would help a lot in that regard.
Correct. Another thing to investigate is why the razor file in the repro doesn't emit trimmer warnings even though it is calling trim-unsafe APIs. We want to be emphatic with customers that they cannot reliably depend on reflection in trimmed applications and they should be moving to using the source generator instead -- emitting the relevant warnings would help a lot in that regard.
Blazor predates many of the linking/trimming work (in fact, it spearheaded it) and does not support full trimming. For example, we don't trim customers dlls to prevent issues like this.
While I believe this is a regression, I am going to suggest we patch it on our side, since I think it'll be easier.
Is there an option to instruct the linker to specifically not trim away constructor parameters?
Is there an option to instruct the linker to specifically not trim away constructor parameters?
--keep-metadata parametername
this will avoid removing names from method parameters. It obviously comes with size implication. Note that this means that the method in question is not considered accessible via reflection - otherwise we would keep its method parameters intact.
With the above repro why do the following settings from Configure the Linker for ASP.NET Core Blazor not prevent method parameters from being stripped?
<PropertyGroup>
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
</PropertyGroup>
<ItemGroup>
<BlazorLinkerDescriptor Include="LinkerConfig.xml" />
</ItemGroup>
Where assembly for KeyValuePair is specified:
<?xml version="1.0" encoding="UTF-8" ?>
<linker>
<assembly fullname="System.Runtime">
<type fullname="System.Collections.Generic*" />
</assembly>
</linker>