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 にすることで、正常に動作する事が確認出来ました。
Thanks Unity forum.
これで解決と思いきや、iOS でも確認しようと設定を切り替えると、iOS は、IL2CPP 固定となり振り出しに戻る。
「 Newtonsoft JSON には、IL2CPP のような AOT ランタイムで動作するモードがあると思います。」
との事なので、結局のところ IL2CPP を使う事で、ストリッピング機能によりリフレクションで関節的にコールしているメソッド等が削除される事が原因のようなので、これを抑止する事にします。
Newtonsoft.Json には、IL2CPP版がありこちらをインストールします。
Newtonsoft.Json-for-Unity
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));
}