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

Serialize/Deserialize not working in Release build #2912

Open
blmiles opened this issue Nov 4, 2023 · 7 comments
Open

Serialize/Deserialize not working in Release build #2912

blmiles opened this issue Nov 4, 2023 · 7 comments

Comments

@blmiles
Copy link

blmiles commented Nov 4, 2023

I have a Maui app project targeting Android and iOS.
The Android version using latest Json libraries is live and running. All works in both debug and release mode.

VS 2022 (latest version), NewtonSoft.Json version 13.0.3, Maui targeting .Net7

The iOS version works in Debug mode but won't serialize/deserialise objects when compiled in Release mode.

I'm not sure if this is a bug or a licensing issue for the iOS platform but this all works in the Android targeted app, both in Build and Release mode. Exactly same codebase AND the Android version is also obfuscated in Release mode.

Expected behavior

Serialize/Deserialize objects in Release build.

Actual behavior

Steps to reproduce

                Players = JsonConvert.DeserializeObject<List<Player>>(Drill.PlayersJson);
@elgonzo
Copy link

elgonzo commented Nov 4, 2023

"Not working" is not an adequate description of the problem you are observing. It's just merely stating the result of the problem you observed. If you did not observe anything (i.e., the "actual behavior") besides being able to state "not working", then you you need to do more debugging and troubleshooting to gather more relevant information about your problem.

licensing issue

The Newtonsoft.Json library has no mechanism or features imposing or enforcing licensing restrictions. If there is anything on your end indicating a licensing issue, it's not related to the Newtonsoft.Json library itself.


Just a shot from my hip and the only one speculation i am willing to do here given the scarce problem description: Often, the default build settings for mobile targets include code trimming and perhaps even Native AOT compiling. Newtonsoft.Json, being a library whose functionality mostly depends on reflection, does not gel well with code trimming or Native AOT compiling. Therefore, i'd suggest to verify your build settings and try explicitly disabling code trimming and Native AOT compiling. If this is not possible/feasible or does not resolve the problem, i'd suggest you give System.Text.Json a try, since System.Text.Json is much less dependent on reflection and therefore a better and more natural fit for projects involving code trimming and/or native AOT compiling.

@blmiles
Copy link
Author

blmiles commented Nov 4, 2023

@elgonzo - well, being in release I'm not seeing actual error generated. AND this all works in Debug mode.
All I can say is that the call: DeserializeObject<List<Player>>(Drill.PlayersJson);
does NOT return any objects in Release build but does so in Debug mode.

You have however pointed me in a good direction to investigate and linking and AOT are highly likely the problem.
Thanks for your insight. Matches what others have posted elsewhere. I'm just experimenting with various build options to fix this.

Thx

@elgonzo
Copy link

elgonzo commented Nov 4, 2023

does NOT return any objects in Release build but does so in Debug mode

I assume "not returing any objects" means that DeserializeObject returns null. While this is not conclusive evidence in itself, it would fit my speculation that code trimming / native AOT compiling might be contributing to the problem. It might be that for the debug build, code trimming / NativeAOT might be disabled by default for the step debugger to work properly and meaningfully, possibly explaining the difference in behavior between debug and release build. But again, this is only my "blind" speculation, so please don't take it as authoritative gospel.

@blmiles
Copy link
Author

blmiles commented Nov 4, 2023

I don't know enough about the linker and trimming directives for the csproj. and the VS UI gives limited control on the project.
Removing all linking, the app won't even run on iOS.
Might need to do as you suggest, use System.Text.Json but it has other limitations ... not looking pretty right now.

Are there any directives similar to this to negate the issue effecting .Json?

  <ItemGroup>
    <TrimmerRootAssembly Include="System.Collections.Immutable" />
  </ItemGroup>

Thx

@elgonzo
Copy link

elgonzo commented Nov 4, 2023

<TrimmerRootAssembly> is the correct setting to exclude an assembly from trimming. Note however, that the System.Collections.Immutable assembly is not providing the List<T> used in the code example in your report. It might perhaps also be necessary to exclude your program assembly from trimming -- or perhaps not; it depends entirely on how your program operates with the instances of the types involved in the (de)serialization (it entirely depends on whether the trimming logic can detect the used types and members from the reachable code in your program assembly).

Disabling NativeAOT for iOS might be a bit trickier, and i unfortunately have no experience regarding .NET stuff on iOS. I don't know if .NET's iOS targets still rely on Xamarin.iOS under the hood and supports the mono interpreter. However, as it seems, disabling native AOT compiling for iOS targets requires enabling the mono interpreter. See here for details: https://learn.microsoft.com/en-us/dotnet/maui/macios/interpreter

If this is too difficult to setup correctly (don't ask me how), perhaps switching to System.Text.Json using code generation is an alternative. See here for how to configure code generation for System.Text.Json: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation. System.Text.Json's code generation will not rely on reflection, thus it should avoid issues related to trimming and NativeAOT as far as Json (de)serialization is concerned. It however comes with the restriction of not being able to (de)serialize private/protected members (obviously, as access to those members would again require reflection which in turn would nullify the benefits of STJ code generation and also reintroduce trimming/NativeAOT issues).

@blmiles
Copy link
Author

blmiles commented Nov 4, 2023

You help and advice is excellent! Thanks for pointing me in good directions!
Will mess with it over weekend, Might ask a couple more questions if I may. I will also post my findings if successful!

@blmiles
Copy link
Author

blmiles commented Nov 6, 2023

@elgonzo - update.
I tried every permutation I could to exclude various libraries from linking... to no avail.
Finally swapped out the DeserializeObject<List<Player>>(Drill.PlayersJson); and similar calls for something like this:

                Byte[] playersbytes = Encoding.ASCII.GetBytes(Drill.PlayersJson);
                Stream playerstream = new MemoryStream(playersbytes);
                Players = System.Text.Json.JsonSerializer.Deserialize<LinkedList<Player>>(playerstream);

Had to deal with the Serialize too. Drill.PlayersJson = System.Text.Json.JsonSerializer.Serialize(Players);

So all is good for now. And it all works!
I learned a bit about linking and AOT !!

Thanks for your responses, insight and guidance!

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

No branches or pull requests

2 participants