Help: Queues can not be deserialized on iOS devices #51
-
Expected behaviorQueue is successfully deserialized on iOS devices. Actual behaviorJsonSerializationException is thrown when deserializing generic Queues. Serialization works fine in all environments.
Steps to reproduce
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Utilities;
using UnityEngine;
public class Test : MonoBehaviour
{
private static void TestQueue<T>(JsonSerializerSettings settings)
{
AotHelper.EnsureList<T>();
var queue = new Queue<T>();
queue.Enqueue(default);
var queueJson = JsonConvert.SerializeObject(queue, settings);
Debug.Log(queueJson);
try
{
JsonConvert.DeserializeObject<Queue<T>>(queueJson, settings);
Debug.Log($"JsonConvert.DeserializeObject<Queue<{typeof(T).Name}>>(queueJson, settings);");
}
catch (Exception e)
{
Debug.LogException(e);
}
}
private void Start()
{
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
TestQueue<int>(settings);
TestQueue<string>(settings);
}
}
DetailsHost machine OS running Unity Editor 👉 macOS Catalina 10.15.4, Windows 10 PRO 1909 Checklist
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
Hi @felipegodias, thanks for reporting this. This looks like some tasty glitch. You're both using the EnsureList and explicitly constructing a new Queue type, which are the two easy to spot pitfalls. I cannot give you a straight up answer by just looking at your example. Some ideas comes to mind, but I'll have to sit down with this to further investigate. Probably within a day or two. |
Beta Was this translation helpful? Give feedback.
-
Very rich issue as well. Kudos |
Beta Was this translation helpful? Give feedback.
-
Ha! Interesting find! I was able to reproduce this when building to Windows with IL2CPP. Here's what's going on: The How the official Json .NET package handles this is by trying to obtain the parameterized constructor See in the source for reference, where declaring if it's deserializable actually lands on if it can find the parameterized constructor or not: WorkaroundA quickfix would be to ensure that constructor, for example I was getting it to work as intended with the following: AotHelper.EnsureList<T>();
AotHelper.Ensure(() =>
{
_ = new Queue<T>(Array.Empty<T>());
}); Looking at the implementation of the queue.cs in .NET Framework 4.8 there's no reference to any Long-term fixStill brainstorming about this, but I'll either add some special logic to the main Json .NET code to handle Gets tricky when trying to consider that some may inherit from Eh, these are just brainstorm ideas anyway. I'll figure something out and deploy a 12.0.302 version. If you have any ideas yourself then please share ;) Like how would you as a user want to deal with this? Fine line of allowing fine grained adjustment such as Closing this issue as resolved as there is a workaround and moving the fix over to #53 |
Beta Was this translation helpful? Give feedback.
-
Just wanted to say thank you SO much for this, although it's closed it's still relevant to the official unity version. I switched over to it recently (com.unity.nuget.newtonsoft-json 3.0.1) but whilst everything worked fine in dev, the release build failed horrobly throwing Exceptions when deserializing a Queue with the error Preventing code stripping with If anyone else is struggling to deserialize Collections with the new official version, the AOTHelper Workaround above worked for me (substituting for the type used, here (I don't know if it's important, but I've left all the [Preserve] attributes in above the class constructors anyway, just in case.) public class AotTypeEnforcer : MonoBehaviour
{
public void Awake()
{
// https://github.com/jilleJr/Newtonsoft.Json-for-Unity/wiki/Fix-AOT-using-AotHelper
// Ensures IL2CPP compiles the AOT-compiled classes
AotHelper.EnsureList<MyBaseClass>();
// https://github.com/jilleJr/Newtonsoft.Json-for-Unity/discussions/51#discussioncomment-185183
AotHelper.Ensure(() =>
{
_ = new Queue<MyBaseClass>(Array.Empty<MyBaseClass>());
});
}
} |
Beta Was this translation helpful? Give feedback.
Ha! Interesting find! I was able to reproduce this when building to Windows with IL2CPP. Here's what's going on:
The
Query<>
type is neither anIList<>
nor anICollection<>
, but instead only aIReadOnlyCollection<>
, which makes it so it doesn't contain any of the.Add()
methods that comes withIList<>
andICollection<>
.How the official Json .NET package handles this is by trying to obtain the parameterized constructor
new Queue<T>(IEnumerable<T>)
, which has been omitted by the bytecode stripping when compiling with IL2CPP and therefore you receive the error that it cannot be deserialized.See in the source for reference, where declaring if it's deserializable actually lands on if it can …