Unity ver2020.3.38f1 Personal
GoogleCredential credential = GoogleCredential.FromJson(jsonText).CreateScoped( DriveService.Scope.DriveReadonly // ,DriveService.Scope.Drive );
アカウント情報を利用して GoogleDrive にアクセスすると、Android だと以下のエラーとなります。
2023/09/29 21:44:05.541 4080 4294 Info Unity error:Error deserializing JSON credential data.
実際のエラーは、以下のエラーでした。対応策は一番最後を参照して下さい。
2023/09/29 21:44:05.538 4080 4294 Info Unity Error Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type Google.Apis.Auth.OAuth2.JsonCredentialParameters. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'type', line 2, position 7. 2023/09/29 21:44:05.538 4080 4294 Info Unity at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonObjectContract objectContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, Newtonsoft.Json.Serialization.JsonProperty containerProperty, System.String id, System.Boolean& createdFromNonDefaultCreator) [0x00000] in <00000000000000000000000000000000>:0 2023/09/29 21:44:05.538 4080 4294 Info Unity at 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.JsonContainerCo 2023/09/29 21:44:05.540 4080 4294 Info Unity finally
簡単に言うと、コンストラクターが見つからなかったようです。
上記のソースは、Unity のエミュレータでは動作しているので認証情報のJSONに間違いはないです。
Android 特有のエラーと考えられます。
ソースを clone して確認すると、以下のソースでエラーがあるのが確認できます。
https://github.com/googleapis/google-api-dotnet-client
./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs
google-api-dotnet-client % grep -r "Error deserializing JSON credential data" . ./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs: throw new InvalidOperationException("Error deserializing JSON credential data.", e); ./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs: throw new InvalidOperationException("Error deserializing JSON credential data.", e); ./Src/Support/Google.Apis.Auth/OAuth2/DefaultCredentialProvider.cs: throw new InvalidOperationException("Error deserializing JSON credential data.", e); falco@M1 google-api-dotnet-client %
以下のコードでエラーが発生していることが考えられます。
JsonCredentialParameters credentialParameters; try { credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(json); } catch (Exception e) { throw new InvalidOperationException("Error deserializing JSON credential data.", e); } return CreateDefaultCredentialFromParameters(credentialParameters);
ネット検索すると以下のコードを追加しているようなので、追加 (Assets/link.xml)
https://forum.unity.com/threads/error-deserializing-json-credential-data-google-api.1367799/
<linker> <assembly fullname="Google.Apis.Auth"> <type fullname="Google.Apis.Auth.OAuth2.JsonCredentialParameters" preserve="all" /> </assembly> </linker>
これで実行すると、次に以下のエラーが発生
2023/09/29 22:21:09.984 11023 11057 Info Unity Error System.NotSupportedException: System.Reflection.Emit.DynamicMethod::.ctor 2023/09/29 22:21:09.984 11023 11057 Info Unity at System.Reflection.Emit.DynamicMethod..ctor (System.String name, System.Type returnType, System.Type[] parameterTypes, System.Type owner, System.Boolean skipVisibility) [0x00000] in <00000000000000000000000000000000>:0 2023/09/29 22:21:09.984 11023 11057 Info Unity at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateDynamicMethod (System.String name, System.Type returnType, System.Type[] parameterTypes, System.Type owner) [0x00000] in <00000000000000000000000000000000>:0 2023/09/29 22:21:09.984 11023 11057 Info Unity at Newtonsoft.Json.Utilities.DynamicReflectionDelegateFactory.CreateDefaultConstructor[T] (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 2023/09/29 22:21:09.984 11023 11057 Info Unity at Newtonsoft.Json.Serialization.DefaultContractResolver.InitializeContract (Newtonsoft.Json.Serialization.JsonContract contract) [0x00000] in <00000000000000000000000000000000>:0 2023/09/29 22:21:09.984 11023 11057 Info Unity at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract (System.Type objectType) [0x00000] in <00000000000000000000000000
エラーの原因は以下の模様
Error System.NotSupportedException: System.Reflection.Emit.DynamicMethod::.ctor
再度ネット検索すると、以下がヒット
https://forum.unity.com/threads/notsupportedexception-with-log-detail.543644/
System.Reflection.Emit is not supported with IL2CPP. You will need to use a JSON library that does not require use of it to work with Hololens. I believe that Newtonsoft JSON does have a mode that works with AOT runtimes like IL2CPP.
System.Reflection.Emit は IL2CPP ではサポートされていません。 Hololens と連携するには、JSON ライブラリを使用する必要がありません。 Newtonsoft JSON には、IL2CPP のような AOT ランタイムで動作するモードがあると思います。
結局のところ、Scripting Backend を IL2CPP -> Mono にすることで、正常に動作する事が確認出来ました。
これで解決と思いきや、iOS でも確認しようと設定を切り替えると、iOS は、IL2CPP 固定となり振り出しに戻る。
「 Newtonsoft JSON には、IL2CPP のような AOT ランタイムで動作するモードがあると思います。」
との事なので、結局のところ IL2CPP を使う事で、ストリッピング機能によりリフレクションで関節的にコールしているメソッド等が削除される事が原因のようなので、これを抑止する事にします。
Newtonsoft.Json には、IL2CPP版がありこちらをインストールします。
Assets 配下の Link.xml に以下を記述することで、Android、iOS で動作します。
<linker> <assembly fullname="Google.Apis.Auth" preserve="all" /> <assembly fullname="Google.Apis.Drive.v3" preserve="all" /> <assembly fullname="Newtonsoft.Json" preserve="all" /> </linker>
以下、抜粋のGoogle Drive にアクセスするソースです。
void Start() { ListFilesRecursively("folderId"); } private void InitGooglDrive() { String jsonText = Resources.Load<TextAsset>("GoogleAuth/service_account").text; Debug.Log($"json:{jsonText}"); JsonCredentialParameters credentialParameters; try { credentialParameters = NewtonsoftJsonSerializer.Instance.Deserialize<JsonCredentialParameters>(jsonText); } catch (Exception e) { Debug.Log($"Error {e}"); } finally { Debug.Log($"finally"); } // OAuth 2.0認証をセットアップ Debug.Log("Call FromStream"); GoogleCredential credential = GoogleCredential.FromJson(jsonText).CreateScoped( DriveService.Scope.DriveReadonly ); // Drive APIクライアントを作成 service = new DriveService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "Google shared folder viewer" }); } private async void ListFilesRecursively(string folderId) { InitGooglDrive(); Debug.Log("folder:" + folderId); FilesResource.ListRequest listRequest = service.Files.List(); listRequest.Q = $"'{folderId}' in parents"; listRequest.Fields = "nextPageToken, files(id, name, mimeType, modifiedTime, md5Checksum)"; do { var files = listRequest.Execute().Files; if (files == null || files.Count == 0) { Debug.Log("No files found in folder: " + folderId); continue; } foreach (var file in files) { Debug.Log($"File Name: {file.Name}, ID: {file.Id}, MIME Type: {file.MimeType}, createdTime:{file.CreatedTime}"); // フォルダの場合、再帰的にその中身を表示 if (file.MimeType == "application/vnd.google-apps.folder") { ListFilesRecursively(file.Id); } } if(listRequest.PageToken != null) { // next page token があればスリープ await Task.Delay(200); } } while (!string.IsNullOrEmpty(listRequest.PageToken)); }