applejag/Newtonsoft.Json-for-Unity

Help: Can't really figure out which Type `MethodAccessException` needs in AOT based on error message

Bersaelor opened this issue · 4 comments

Expected behavior

I want to deserialize a class from json, which looks like the following:

[System.Serializable]
public class TargetSettings {
    public bool? allowModelSearch;
    public Dictionary<string, string>? colorTheme;
    public string? lightEnvironment;

    public static TargetSettings CreateFromJSON(string jsonString) {
        return JsonConvert.DeserializeObject<TargetSettings>(jsonString);
    }
}

Actual behavior

It works in the editor and on android, but on WebGL I get the following error

mylooc-web.framework.js:2 MethodAccessException: Attempt to access method 'System.Collections.Generic.ICollection<Newtonsoft.Json.Serialization.JsonProperty>.get_IsReadOnly' on type '' failed.
  at System.Collections.ObjectModel.Collection`1[T].Add (T item) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.JsonPropertyCollection.AddProperty (Newtonsoft.Json.Serialization.JsonProperty property) 
Full stacktrace:
MethodAccessException: Attempt to access method 'System.Collections.Generic.ICollection<Newtonsoft.Json.Serialization.JsonProperty>.get_IsReadOnly' on type '' failed.
  at System.Collections.ObjectModel.Collection`1[T].Add (T item) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.JsonPropertyCollection.AddProperty (Newtonsoft.Json.Serialization.JsonProperty property) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateProperties (System.Type type, Newtonsoft.Json.MemberSerialization memberSerialization) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract (System.Type objectType) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateContract (System.Type objectType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Func`2[T,TResult].Invoke (T arg) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Collections.Concurrent.ConcurrentDictionary`2[TKey,TValue].GetOrAdd (TKey key, System.Func`2[T,TResult] valueFactory) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Utilities.ThreadSafeStore`2[TKey,TValue].Get (TKey key) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.DefaultContractResolver.ResolveContract (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.GetContract (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.GetContractSafe (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject (System.String value, System.Type type, Newtonsoft.Json.JsonSerializerSettings settings) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject[T] (System.String value, Newtonsoft.Json.JsonSerializerSettings settings) [0x00000] in <00000000000000000000000000000000>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject[T] (System.String value) [0x00000] in <00000000000000000000000000000000>:0 
  at TargetSettings.CreateFromJSON (System.String jsonString) [0x00000] in <00000000000000000000000000000000>:0 
  at TargetSettingsManager.Startup (NetworkService service) [0x00000] in <00000000000000000000000000000000>:0 
  at Managers+<StartupManagers>d__52.MoveNext () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.MonoBehaviour.StartCoroutineManaged2 (System.Collections.IEnumerator enumerator) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.MonoBehaviour.StartCoroutine (System.Collections.IEnumerator routine) [0x00000] in <00000000000000000000000000000000>:0 
  at Managers.Awake () [0x00000] in <00000000000000000000000000000000>:0 
UnityEngine.MonoBehaviour:StartCoroutineManaged2(IEnumerator)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
Managers:Awake()

Now I get the whole JiT an AOT problem with IL2CPP and WebGL, and have fixed errors before where the type that was needed was mentioned more clearly in the error stack trace.
What I am most curious about is how to read the necessary linker additions from the above stack trace?
Especially since the first line of the error says on type '' failed , the Type `` is super helpful.

Details

Host machine: MacOS

Unity build target: WebGL

"jillejr.newtonsoft.json-for-unity": "12.0.301"

I was using Unity version 2020.2.3f1

Checklist

  • Shutdown Unity, deleted the /Library folder, opened project again in Unity, and problem still remains.
  • Checked to be using latest version of the package.

Hi @Bersaelor, thanks for such a detailed issue!

That is funny, "on type '' failed". What should be there between the ' ' is the actual type that implements the ICollection interface.

Now, the method it's complaining about, I've never seen it error on before.

I think you see it as well, but it's the ICollection<T>.IsReadOnly property that's presumably being stripped.

I don't think you actually can mark an interface member to be preserved. But I mean you could try the following link.xml file to target the property:

https://github.com/jilleJr/Newtonsoft.Json-for-Unity/wiki/Reference-link.xml#the-property-element

Either that or try preserving the Json .NET intermediate types or something.

I need to look deeper until I can give you a better answer. Good find, and very nice to have a repro! In the meantime: take care!

What I am most curious about is how to read the necessary linker additions from the above stack trace?
Especially since the first line of the error says on type '' failed , the Type `` is super helpful.

From this exception, you can't.

Json .NET uses some types inside itself to build up collections and dictionaries, and that might be what's going on here. Otherwise the dictionary might actually have become stripped, or some subtype that the dictionary maybe uses, for example the Dictionary<TKey,TValue>.KeyCollection.ICollection<TKey>.IsReadOnly Property could've been stripped.

So to try to answer your question, it's only about trying to follow the code paths of the source code of both Json .NET and .NET itself as well to figure out which types and properties are used here and there.

Then after that I always refer back to my wiki page when I need to write some link.xml.

Btw, which stripping level were you building with?

Thanks for the quick reply, i'm relatively novice in Unity, so I was kind of confused.

I also figured it might be the <string, string> dictionary that's causing an error in the types used in the inner workings of JSON .NET, but I was kind of lost looking at the error.

Here are my settings:

Screen Shot 2021-02-09 at 11 19 09

I'm still astonished that Unity make so many things easy compared to other environments, but JSON deserializing, something that I never spent much time or thought on when developing iOS/Android mobile or RoR/React websites is such a problem in Unity 😅

Hehe yeah well Unity is actually having all these problems because of their IL2CPP compiler that they use for iOS and WebGL builds.

The IL2CPP compiler struggles the most with dealing with generics and use of reflection (and rightfully so), but both subjects are something that Json .NET uses a lot of.