JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.73k stars 3.25k forks source link

DeserializeObject exception with IList and empty value {"key"[]} on Android devices. #2865

Open elliot-spacego opened 1 year ago

elliot-spacego commented 1 year ago

I encountered an error in Unity after updating to a newest version 2021.3.26. I noticed that the update of Unity also updated the version of Newtonsoft.Json from 13.0.1 to 13.0.2. Edit: this only happens to me on Android devices. In Unity Editor it works fine.

Source/destination JSON

JSon received from our server:

{"userCredits":150000,"userStars":2552,"reward":[]}

Source/destination Code

// _responseServer == {"userCredits":150000,"userStars":2552,"reward":[]}
var redeemResponse = JsonConvert.DeserializeObject<RedeemResponse>(_responseServer);

// Class
[Serializable]
public class RedeemResponse
{
    public IList<Redeemable> reward;
    public long userCredits;
    public int userStars;
}

[Serializable]
public class Redeemable
{
    public string type;
    public string idReward;
}

Expected behavior

var redeemResponse is correctly instantiated and follows the code flow.

Actual behavior

Exception: ArgumentNullException: Value cannot be null.
Parameter name: method
Newtonsoft.Json.Utilities.ValidationUtils.ArgumentNotNull (System.Object value, System.String parameterName) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Utilities.ExpressionReflectionDelegateFactory.CreateParameterizedConstructor (System.Reflection.MethodBase method) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonArrayContract.CreateWrapper (System.Object list) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewList (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonArrayContract contract, System.Boolean& createdFromNonDefaultCreator) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, System.Object existingValue, System.String id) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue (Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.JsonConverter propertyConverter, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty, Newtonsoft.Json.JsonReader reader, System.Object target) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject (System.Object newObject, Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, System.String id) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.JsonConvert.DeserializeObject (System.String value, System.Type type, Newtonsoft.Json.JsonSerializerSettings settings) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.JsonConvert.DeserializeObject[T] (System.String value, Newtonsoft.Json.JsonSerializerSettings settings) (at <00000000000000000000000000000000>:0)
APIProcessRequest.RedeemCoinsPurchase () (at <00000000000000000000000000000000>:0)

Steps to reproduce

Just need a serialized class with IList and a JSon with empty list {"key":[]}.

// Your calls to Newtonsoft.Json here
// _responseServer == {"userCredits":150000,"userStars":2552,"reward":[]}
var redeemResponse = JsonConvert.DeserializeObject<RedeemResponse>(_responseServer);

Possible Solution

After running some tests, I have noticed that if I change the IList to a List, everything works fine. I understand that there might be an issue with the interface. I have many classes with interfaces, and I would like to know if this is a fixable error or if I have to change all the interfaces.

elgonzo commented 1 year ago

Not reproducible with vanilla Newtonsoft.Json 13.0.2 in dotnetfiddle (https://dotnetfiddle.net/5hSnzB)

Two immediate things come to my mind.

  1. Either, the actual data and class types involved in your deserialization error are not the same as you presented in your report, making it very difficult to get to the bottom of the problem as the circumstances that trigger this error would remain unknown to us.

  2. Or, the Newtonsoft.Json version you are using is not the vanilla library but a modified one. In that case, use the vanilla library directly as provided by James Newton-King (JamesNK) and not some version offered by some other third parties. (If you knowingly use a Newtonsoft.Json version modified by a third party and you want/need to keep using it, you will have to contact this third party with your problem, because apparently the original vanilla library does not seem to suffer from this issue.)

(Disclaimer: I am not the author nor maintainer of the Newtonsoft.Json library. I am also not associated or otherwise involved in any way with the library or its author. I am just a user...)

elliot-spacego commented 1 year ago

I haven't mentioned that this only happens to me on Android devices. In Unity Editor it works fine. So, in console it is normal that it works well. Unity uses vanilla library I think.

elgonzo commented 1 year ago

Hmm, i am just speculating now: Perhaps your project is set up to trim the code for the Android target to yield the smallest package size there. Just for testing and confirming (or disproving) my speculation, can you check whether code trimming is active for your Android build target? And if that is the case, is the error still reproducible when you deactivate code trimming for the Android build target?

elliot-spacego commented 1 year ago

I use the minimal level.

elgonzo commented 1 year ago

I use the minimal level.

What if you disable it completely? Any change?

Edit: Hmm, reading the documentation at https://docs.unity3d.com/Manual/ManagedCodeStripping.html, it seems disabling is only possible when using the Mono scripting backend. I have no exeperience with Unity and Android to even contemplate whether Mono scripting backend is available on Android targets and what the possible effects could be when switching from another scripting backend to the Mono scripting backend. I am sorry for not being able to be more helpful :-(

Netbul4 commented 7 months ago

Any update on this issue?