Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AOT and trimming-related deserialization issues #91

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

thoron
Copy link
Contributor

@thoron thoron commented Sep 13, 2024

In this PR I want to suggest a mitigation for AOT compilation issues for non-reflection-based deserialization and missing parameterless constructors for reflection-based deserialization.

Unhandled Exception: System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property.
   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled() + 0x27
   at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer() + 0xc
   at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions, Type) + 0x2d
   at System.Text.Json.JsonSerializer.GetTypeInfo[T](JsonSerializerOptions) + 0x2b
   at System.Text.Json.JsonSerializer.Deserialize[TValue](String, JsonSerializerOptions) + 0x27
   at DeviceId.Linux.Components.LinuxRootDriveSerialNumberDeviceIdComponent.GetValue() + 0x30
   at DeviceId.Formatters.HashDeviceIdFormatter.<>c.<GetDeviceId>b__4_1(KeyValuePair`2 x) + 0x18
   at System.Linq.Enumerable.SelectIPartitionIterator`2.PreallocatingToArray(Int32) + 0x71
   at System.Linq.Enumerable.SelectIPartitionIterator`2.ToArray() + 0x55
   at DeviceId.Formatters.HashDeviceIdFormatter.GetDeviceId(IDictionary`2) + 0xd8

For a AOT project settings

<PropertyGroup>
	<TrimmerSingleWarn>false</TrimmerSingleWarn>
	<PublishAot>true</PublishAot>
	<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
	<BuiltInComInteropSupport>true</BuiltInComInteropSupport>  
</PropertyGroup>

These build warnings:

ILC : Trim analysis warning IL2026: DeviceId.Linux.Components.LinuxRootDriveSerialNumberDeviceIdComponent.GetValue(): Using member 'System.Text.Json.JsonSerializer.Deserialize<LinuxRootDriveSerialNumberDeviceIdComponent.LsblkOutput>(String,JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved. 

And basically the same warning but different error when trying to force reflections using <JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>.

Unhandled Exception: System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'DeviceId.Linux.Components.LinuxRootDriveSerialNumberDeviceIdComponent+LsblkOutput'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
 ---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'DeviceId.Linux.Components.LinuxRootDriveSerialNumberDeviceIdComponent+LsblkOutput'.
   --- End of inner exception stack trace ---
   at System.Text.Json.ThrowHelper.ThrowNotSupportedException(ReadStack&, Utf8JsonReader&, NotSupportedException) + 0x2bf
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&) + 0xc9
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&, Boolean&) + 0x235
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&, JsonSerializerOptions, ReadStack&) + 0x345
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo`1, Nullable`1) + 0xea
   at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo`1) + 0xd9
   at System.Text.Json.JsonSerializer.Deserialize[TValue](String, JsonSerializerOptions) + 0x3a
   at DeviceId.Linux.Components.LinuxRootDriveSerialNumberDeviceIdComponent.GetValue() + 0x30
   at DeviceId.Formatters.HashDeviceIdFormatter.<>c.<GetDeviceId>b__4_1(KeyValuePair`2 x) + 0x18
   at System.Linq.Enumerable.SelectIPartitionIterator`2.PreallocatingToArray(Int32) + 0x71
   at System.Linq.Enumerable.SelectIPartitionIterator`2.ToArray() + 0x55
   at DeviceId.Formatters.HashDeviceIdFormatter.GetDeviceId(IDictionary`2) + 0xd8

Thanks for a nice piece of software!

Original commit message

- Implemented DefaultJsonTypeInfoResolver to register LsblkOutput and LsblkDevice types.
- Addressed errors related to parameterless constructors with <PublishTrimmed>true</PublishTrimmed>.
- Set custom object creation to avoid reflection issues when <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault> is used.
- Ensured compatibility with AOT compilation and trimming for JSON deserialization.

- Implemented DefaultJsonTypeInfoResolver to register LsblkOutput and LsblkDevice types.
- Addressed errors related to parameterless constructors with <PublishTrimmed>true</PublishTrimmed>.
- Set custom object creation to avoid reflection issues when <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault> is used.
- Ensured compatibility with AOT compilation and trimming for JSON deserialization.
@MatthewKing
Copy link
Owner

Thanks for this PR. I'm currently swamped with work but I will get it reviewed and merged as soon as I have some free time. Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants