question

Takuya Nakajo avatar image
Takuya Nakajo asked

Invalid JSON string error when executing ExecuteFunction

When I run ExecuteFunction, the Azure Functions process finishes successfully, but I get an Invalid JSON string error before entering the callback process on success of ExecuteFunction. After debugging, it seems that the cause is that the responseText in PlayFabUnityHttp.cs is garbled. I'm having trouble solving this problem. Is there a problem with the way I've built my environment?

Environment

  • Unity 2021.1.11f1
  • PlayFab Unity SDK 2.116.211012
  • PlayFabAllSDK 1.103.211012

Unity Error Log

SerializationException: Invalid JSON string
PlayFab.Json.PlayFabSimpleJson.DeserializeObject (System.String json) (at Assets/PlayFabSDK/Shared/Internal/SimpleJson.cs:570)
PlayFab.Json.PlayFabSimpleJson.DeserializeObject (System.String json, System.Type type, PlayFab.Json.IJsonSerializerStrategy jsonSerializerStrategy) (at Assets/PlayFabSDK/Shared/Internal/SimpleJson.cs:602)
PlayFab.Json.PlayFabSimpleJson.DeserializeObject[T] (System.String json, PlayFab.Json.IJsonSerializerStrategy jsonSerializerStrategy) (at Assets/PlayFabSDK/Shared/Internal/SimpleJson.cs:610)
PlayFab.Json.SimpleJsonInstance.DeserializeObject[T] (System.String json) (at Assets/PlayFabSDK/Shared/Internal/ISerializer.cs:85)
PlayFab.Internal.PlayFabUnityHttp.OnResponse (System.String response, PlayFab.Internal.CallRequestContainer reqContainer) (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabUnityHttp.cs:180)
UnityEngine.Debug:LogException(Exception)
PlayFab.Internal.PlayFabUnityHttp:OnResponse(String, CallRequestContainer) (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabUnityHttp.cs:224)
PlayFab.Internal.<Post>d__12:MoveNext() (at Assets/PlayFabSDK/Shared/Internal/PlayFabHttp/PlayFabUnityHttp.cs:157)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr) (at /Users/bokken/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17) 

Client Code

void Start()
{
    PlayFabClientAPI.LoginWithCustomID(new LoginWithCustomIDRequest
    {
        CustomId = "200",
        CreateAccount = true,
    }, result =>
    {
        Debug.Log("ログイン成功");
        CallAzureFunctions<ModifyUserVirtualCurrencyResult>(
        "AddUserVirtualCurrency",
        null,
        funcResult =>
        {
            Debug.Log("仮想通貨の更新完了");
        }, funcError =>
        {
            Debug.Log("実行時にエラーが発生しました。");
            Debug.Log(funcError.GenerateErrorReport());
        });
    }, error =>
    {
        Debug.Log(error.GenerateErrorReport());
    });
}


public void CallAzureFunctions<T>(
        string funcName,
        object funcParam,
        Action<T> onSuccess = null,
        Action<PlayFabError> onError = null)
{
    PlayFabCloudScriptAPI.ExecuteFunction(new ExecuteFunctionRequest()
    {
        Entity = new PlayFab.CloudScriptModels.EntityKey()
        {
            Id = PlayFabSettings.staticPlayer.EntityId,
            Type = PlayFabSettings.staticPlayer.EntityType
        },
        FunctionName = funcName,
        FunctionParameter = funcParam,
        GeneratePlayStreamEvent = true
    }, result =>
    {
        Debug.Log("Azure Functions Success! ");
        var jsonResult = (JsonObject)result.FunctionResult;
        var funcSuccess = PlayFabSimpleJson.DeserializeObject<T>(jsonResult["result"].ToString());
        var funcError = PlayFabSimpleJson.DeserializeObject<PlayFabError>(jsonResult["error"].ToString());
        if (result.FunctionResultTooLarge != null && (bool)result.FunctionResultTooLarge)
        {
            onError?.Invoke(funcError);
            return;
        }
        if (funcError == null)
            onSuccess?.Invoke(funcSuccess);
        else
            onError?.Invoke(funcError);
    }, error =>
    {
        onError.Invoke(error);
    });
}

Server Code

[FunctionName("AddUserVirtualCurrency")]
public static async Task<dynamic> AddUserVirtualCurrency(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
    string body = await req.ReadAsStringAsync();
    var context = JsonConvert.DeserializeObject<FunctionExecutionContext<dynamic>>(body);
    var args = context.FunctionArgument;
    string playFabId = context.CallerEntityProfile.Lineage.MasterPlayerAccountId;
    var serverApi = GetServerInstance(context.TitleAuthenticationContext);

    var result = await serverApi.AddUserVirtualCurrencyAsync(new AddUserVirtualCurrencyRequest()
    {
        PlayFabId = playFabId,
        VirtualCurrency = "GD",
        Amount = 1000
    });

    return new
    {
        error = PlayFabSimpleJson.SerializeObject(result.Error),
        result = PlayFabSimpleJson.SerializeObject(result.Result)
    };
}

private static PlayFabServerInstanceAPI GetServerInstance(TitleAuthenticationContext titleAuthenticationContext)
{
    var apiSettings = new PlayFabApiSettings
    {
        TitleId = titleAuthenticationContext.Id,
        DeveloperSecretKey = Environment.GetEnvironmentVariable("PLAYFAB_DEV_SECRET_KEY", EnvironmentVariableTarget.Process),
    };

    return new PlayFabServerInstanceAPI(apiSettings);
}


Server Log

[2021-12-11T02:52:49.982] Executing 'ExecuteFunction' (Reason='This function was programmatically called via the host APIs.', Id=0bd10fac-751e-4141-a7d6-540dedfe0126)
[2021-12-11T02:52:51.178] Executing 'AddUserVirtualCurrency' (Reason='This function was programmatically called via the host APIs.', Id=fdd7dbc6-232f-4fa7-a555-5b89b3b7cdd4)
[2021-12-11T02:52:52.195] Executed 'AddUserVirtualCurrency' (Succeeded, Id=fdd7dbc6-232f-4fa7-a555-5b89b3b7cdd4, Duration=1015ms)
[2021-12-11T02:52:52.263] Executed 'ExecuteFunction' (Succeeded, Id=0bd10fac-751e-4141-a7d6-540dedfe0126, Duration=2398ms)

PlayFabUnityHttp.cs

6 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.

Gosen Gao avatar image Gosen Gao commented ·

I have tested your code with the same environment, but I can't reproduce this issue, everything works fine for me. Would you please tell me in which step does this error occur? Have the VirtualCurrency changed? We don’t recommend using PlayFabSimpleJson, could you please try to replace PlayFabSimpleJson.DeserializeObject with PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer).DeserializeObject to see if it works?

0 Likes 0 ·
Takuya Nakajo avatar image Takuya Nakajo Gosen Gao commented ·

Thank you for your reply. The virtual currency is updated successfully, but I get an error before entering the ExecuteFunction on success callback process (no "Azure Functions Success!" log). I have tried replacing DeserializeObject and SerializeObject, but there is no change. I am doing local debugging.

The procedure is as follows:

1. attach a C# script to the GameObject
2. run debug locally in Azure Functions
3. run Unity debug
4. login succeeds
5. virtual currency update succeeds
6. error occurs

I don't know if this is related, but Unity also output the following two error logs.

0 Likes 0 ·
Gosen Gao avatar image Gosen Gao Takuya Nakajo commented ·

Cloud you please change the request type in the unity extension to see if it works?

0 Likes 0 ·
1.png (18.3 KiB)
Show more comments
Gosen Gao avatar image Gosen Gao commented ·

I tested your code both locally and online, but I still cannot reproduce this issue. You mentioned that the response you got in Unity is unreadable code, it seems to be an encoding issue. Could you please try to use Postman to call the ExecuteFunction API to see if you can get the correct response?

As for the other errors, I encountered the same error in my test. But the code works fine, I think it is not related.

0 Likes 0 ·

1 Answer

·
JayZuo avatar image
JayZuo answered

It seems you are encountering the same issue as Error when using local CloudScript on Azure Functions - Playfab Community.

When using UnityWebRequest, it will handle Accept-Encoding header automatically. This is like a black box as in my test, the header is set to "deflate, gzip", while in the test of @Gosen Gao, it's always set to "identity". So, he cannot reproduce your issue.

Anyway, you can add a breakpoint in CompressResponseBody method and check if encodingsString contains "gzip".

 string encodingsString = request.Headers["Accept-Encoding"];

If it's the same issue as above, please update LocalExecuteFunction as I've mentioned to solve it.

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.

Takuya Nakajo avatar image Takuya Nakajo commented ·

It worked fine. Thank you very much for your help.

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.