NewtonsoftJson got error when serializing a plain simple Object?

Following code is working on PC Unity Editor.

using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;

public class JsonTest : MonoBehaviour
{
    void Start()
    {
        this.testSerializeTestObjectWithInitValues();
        this.testSerializeTestObjectEmpty();        
    }

    [Serializable]
    class TestObject{
        public int a;
    }

    void testSerializeTestObjectWithInitValues(){
        try{
            string jsonStr = JsonConvert.SerializeObject(new TestObject(){
                a = 0
            });
            Debug.Log($"testSerializeTestObject jsonStr: {jsonStr}");
        }catch(Exception e){
            Debug.LogError($"testSerializeTestObject JsonConvert.SerializeObject serializaing failed");
            Debug.LogException(e);
        }
    }

    void testSerializeTestObjectEmpty(){
        try{
            string jsonStr = JsonConvert.SerializeObject(new TestObject(){
                a = 0
            });
            Debug.Log($"testSerializeTestObjectEmpty jsonStr: {jsonStr}");
        }catch(Exception e){
            Debug.LogError($"testSerializeTestObjectEmpty JsonConvert.SerializeObject serializaing failed");
            Debug.LogException(e);
        }
    }

}

But after bulding, then running it on Android, got error:
[quote]
NullReferenceException: Object reference not set to an instance of an object.
System.Linq.Expressions.Interpreter.LightLambda.MakeRunDelegateCtor (System.Type delegateType) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.LightLambda.GetRunDelegateCtor (System.Type delegateType) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Interpreter.LightLambda.MakeDelegate (System.Type delegateType) (at <00000000000000000000000000000000>:0)
System.Linq.Expressions.Expression`1[TDelegate].Compile (System.Boolean preferInterpretation) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.ExpressionValueProvider.GetValue (System.Object target) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonContainerContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.Serialization.JsonContract& memberContract, System.Object& memberValue) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract collectionContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.JsonSerializer.SerializeInternal (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.JsonConvert.SerializeObjectInternal (System.Object value, System.Type type, Newtonsoft.Json.JsonSerializer jsonSerializer) (at <00000000000000000000000000000000>:0)
JsonTest.testSerializeTestObjectWithInitValues () (at <00000000000000000000000000000000>:0)
JsonTest.Start () (at <00000000000000000000000000000000>:0)
Rethrow as JsonSerializationException: Error getting value from 'a' on 'JsonTest+TestObject'.
Newtonsoft.Json.Serialization.ExpressionValueProvider.GetValue (System.Object target) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonContainerContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.Serialization.JsonContract& memberContract, System.Object& memberValue) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract collectionContract, Newtonsoft.Json.Serialization.JsonProperty containerProperty) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.JsonSerializer.SerializeInternal (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) (at <00000000000000000000000000000000>:0)
Newtonsoft.Json.JsonConvert.SerializeObjectInternal (System.Object value, System.Type type, Newtonsoft.Json.JsonSerializer jsonSerializer) (at <00000000000000000000000000000000>:0)
JsonTest.testSerializeTestObjectWithInitValues () (at <00000000000000000000000000000000>:0)
JsonTest.Start () (at <00000000000000000000000000000000>:0)
JsonTest:Start()
[/quote]

NewtonSoft JSON library is installed in Package Manager by using "Add package from git URL", its version is 3.2.0.
Unity version is 2020.3.48f1.

Your "TestObject" class is a nested private class. So it can not be used from outside the outer class. So the serializer probably doesn't even see the class.

Avoid using nested classes, this generally complicates matters and also make sure your class is public.

JSON.Net doesn't have a problem with private nested classes, so I doubt that's the problem.

What scripting backend are you using? I suspect the TestObject class is being stripped during the build and then the expression compiler trips on the null type. Try adding the [Preserve] attribute to the TestObject class.

1 Like


You're probably right, though I actually doubt that the issue is the TestObject class then. I just checked the callstack and the issue i during serialization, not deserialization. Since the constructor of the TestObject is called "classically", it would not be stripped. Also the serializer would not need to mess with that object's constructor. So I guess it's one of the JSON.Net classes that may be stripped. We can see that the serializer tries to create a delegate of a constuctor dynamically. It may try to dynamically instantiate some kind of internal value provider / handler which was stripped.

So I guess that he needs to preserve the whole Json.NET library. For the various ways to accomplish this, see the documentation.

ps: Yes, he's most likely using IL2CPP since it's an android build and that's the default for android for a while now. So stripping is probably the reason.

It doesn't have to do with the constructor. The ExpressionValueProvider in the call stack uses the Linq expression compiler to speed up member access, in this case trying get the value of the "a" field. The runtime probably needs some type information in this case and trips on some stripped information being null.

The code stripping is pretty intelligent, a simple use of a type won't prevent it from being stripped. If the stripping can determine the type or its objects are never actually used, it can still get stripped. Since the object is only ever passed into SerializeObject and then accessed through code generation that the stripper probably doesn't understand, I suspect it determines the object is never actually used and strips it, which in turn means the type itself is never used and gets stripped as well.

The Json.Net is also from Unity's own package, which I would expect to include all the necessary bits to prevent it from being stripped inadvertently.

1 Like