question

Michael avatar image
Michael asked

RuntimeBinderException while Deserializing response to class from Azure Functions

Hello,

I cannot seem to correctly handle the response from Azure Functions. Currently, I'm having Functions read a JSON file, and passing it in it's entirety back to Unity/PlayFab. I can reserialize it using the dynamic type, and the resulting variable handles it correctly, I believe becoming of type JArray. (and by handling it correctly, I mean that the examining the variable in debugger, it shows keys and values as expected.)

However, if I try to deserialize to the class, either the result.FunctionResult.ToString(), or the variable that I deserialized it to, I receive the error (listed below).

I'm confident the data types in the json and class should be correct (no strings into ints), as I've worked with the data for a while. I've even tried manually hardcoding in a very simple json string to be passed back and deserialized, but recieved the same error.

I'm just very confused, because I'm studying the TicTacToe examples from github, and it seems like I'm doing it the same way. But I'll be very grateful if someone points out any errors!

Error

RuntimeBinderException: The best overloaded method match for 'PlayFab.Json.PlayFabSimpleJson.DeserializeObject<POI>(string, PlayFab.Json.IJsonSerializerStrategy)' has some invalid arguments
(wrapper dynamic-method) System.Object.CallSite.Target(System.Runtime.CompilerServices.Closure,System.Runtime.CompilerServices.CallSite,System.Type,object)
System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet] (System.Runtime.CompilerServices.CallSite site, T0 arg0, T1 arg1) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
DemoScript.<ContactFunctions>b__25_0 (PlayFab.CloudScriptModels.ExecuteFunctionResult result) (at Assets/PlayFabPartySDK/Examples/DemoScript.cs:309)
PlayFab.Internal.PlayFabHttp+<>c__DisplayClass23_0`1[TResult].<_MakeApiCall>b__1 () (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabHTTP.cs:215)
PlayFab.Internal.PlayFabUnityHttp.OnResponse (System.String response, PlayFab.Internal.CallRequestContainer reqContainer) (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabUnityHttp.cs:247)
UnityEngine.Debug:LogException(Exception)
PlayFab.Internal.PlayFabUnityHttp:OnResponse(String, CallRequestContainer) (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabUnityHttp.cs:251)
PlayFab.Internal.<Post>d__12:MoveNext() (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabUnityHttp.cs:198)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

Azure Function (simplified)

[FunctionName("TestFunction1")]
 public static async Task<string> Run(
 [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req, Binder binder, ILogger log)
 {
 	string gameEvent = "Zmeer2103";
 	var eventDir = $"events/{gameEvent}/";
 	foreach (var filename in Enum.GetNames(typeof(DataFiles)))
 	{
 		var file = eventDir + gameEvent + '_' + filename + ".json";
 		using (var reader = await binder.BindAsync<TextReader>(new BlobAttribute(file, FileAccess.Read)))
 		{
 			string fileRead = reader.ReadToEnd();
 
 			dataString = fileRead;
 		}
	 } 
	return dataString;
 } 

C# Unity/PlayFab

 public void ContactFunctions()
 {
 	var request = new ExecuteFunctionRequest
 	{
 		FunctionName = "TestFunction1",
	};
            PlayFabCloudScriptAPI.ExecuteFunction(request,
                (result)=>
                {     
		    // THIS WORKS
                    var response = PlayFabSimpleJson.DeserializeObject<dynamic>(result.FunctionResult.ToString());
		    
 		    // THIS ERRORS
                    POI suspects = PlayFabSimpleJson.DeserializeObject<POI>(response);



		    // THIS WAS AN ATTEMPT TO HANDLE THE FULL JSON, WHICH HAS MANY OBJECTS in the class.
                    // List<POI> suspects = new List<POI>();
                    // foreach(var poi in response["Suspects"])
                    // {
                    //     test = PlayFabSimpleJson.DeserializeObject<POI>(poi);
                    //     suspects.Add(test);
                    // }

                },
                onPlayFabError);

        }


unity3dCloudScript
1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Michael avatar image Michael commented ·

And, if it's helpful, here's the class:

POI

[System.Serializable]
public class POI
{
                   
private int id; private string fName; private string lName; private string image; private string lat; private string lng; private float radius; private bool puzzleRequired; private string puzzle; private string text1; private string text2; private string text3; private bool active; private bool suspect; private bool hidden; private bool considered; private bool puzzleAttempted; private bool puzzleSolved; private bool correctAnswer; }

It does take a lot of params, which I thought might be source (a mis-typed param), but like I said, I also did a simpler test with a class of only three strings).

Here's the JSON:

 {
 "id": 0,
 "fName": "Michael",
 "lName": "Lastname",
 "image": "",
 "lat": 55,
 "lng": 5,
 "radius": 3,
 "puzzleRequired": true,
 "puzzle": "Slide_uRabbit",
 "text1": "Hey, It's me.",
 "text2": "You solved the puzzle!",
 "text3": "You failed the puzzle!",
 "active": true,
 "suspect": true,
 "hidden": false,
 "considered": true,
 "puzzleAttempted": false,
 "puzzleSolved": true,
 "correctAnswer": false
 }
0 Likes 0 ·

1 Answer

·
JayZuo avatar image
JayZuo answered

Your POI class does not match your JSON. For example, "lat" is an int in your JSON, but in your code, you set its type to string.

Besides, it's not recommended to use "PlayFabSimpleJson.DeserializeObject" directly. Instead, we'd recommend using

PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer).DeserializeObject
3 comments
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Michael avatar image Michael commented ·

Firstly, you're right, I did have strings going into ints. I also caught that after I posted. I fixed this, but receive the same error. I had even before hand made a simple class with three strings, and a simple json to fit into it, and also received the same error.

As for the replacement line of code, I hadn't seen that before. Is there documentation for it? I've been following the official examples, which use "PlayFabSimpleJson.DeserializeObject" directly, as I had.

TicTacToe - Azure Functions repo

TicTacToe - Unity Repo

TicTacToe - AIMoveHandler.cs //A direct link to where it's used in code.

However, I tried replacing my line with the one you shared, but I still receive the same error. And again, I tried this with my original data, and a simplified data (listed below).

ContactFunctions:

var serializer = PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer);
POI suspects = serializer.DeserializeObject<POI>(response);

Simple Json string:

 string testJson = "{\"one\": \"0\", \"two\": \"Michael\", \"three\": \"Good\"}";


TestClass:

public class TestClass
 {
	private string one;
	private string two;
	private string three;
}
1 Like 1 ·
JayZuo avatar image JayZuo ♦ Michael commented ·

The fields in your class are private, please set them to public.

0 Likes 0 ·
Michael avatar image Michael JayZuo ♦ commented ·

Yes, the TestClass only has private fields, but the actual class POI does have public variables. But this did lead to me finding a second, hidden layer of problem.

But thankfully, we've figured out and solved both of them. The core issue I was having was because of a small, and painfully obvious in retrospect thing. I didn't include "ToString()" on the second deserialization. Originally, because I thought it wasn't necessary, but then I forgot about it, so I didn't test if this was the issue (until just now).

So, ContactFunctions ln. 14 should be:

var serializer =PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer);
POI poi = serializer.DeserializeObject<POI>(response.ToString());//Added "ToString()" here

I would have had a second problem, but you're mentioning of private/public made me realize that my keys/class variables had different letter casing, and won't sync as such. My json had the lower case keys ("fName"), while my class' public variables had uppercase ("FName"). Like this, it won't throw an error, but the resulting class will turn up empty.

Thank you @Jay Zuo for your help, and letting me know about the proper deserialization procedure!

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.