.NET-Core GoogleCloudMlV1PredictRequest Execture Method Returns null Response

Execute GoogleCloudMlV1PredictRequest gives a GoogleApiHttpBody object that has null fields

While creating a .NET-Core MVC API to interact with my Google Cloud ml-engine model, I came across a strange issue. For testing purposes, I used Google’s Census example to get up to speed and make sure what I wanted to do works.

In .NET-Core C#, I used Googles ml API for interacting with the engine. The result code after some trial and error is here:

string credPath = @".\appkey.json";
var json = File.ReadAllText(credPath);
PersonalServiceAccountCred cr = JsonConvert.DeserializeObject(json);

// Create an explicit ServiceAccountCredential credential
var xCred = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(cr.ClientEmail)
{
    Scopes = new [] {
        CloudMachineLearningEngineService.Scope.CloudPlatform
    }
}.FromPrivateKey(cr.PrivateKey));

var service = new CloudMachineLearningEngineService(new BaseClientService.Initializer
{
    HttpClientInitializer = xCred
});

RootObject toSend = JsonConvert.DeserializeObject("{\"instances\": [{\"age\": 25, \"workclass\": \" Private\", \"education\": \" 11th\", \"education_num\": 7, \"marital_status\": \" Never - married\", \"occupation\": \" Machine - op - inspct\", \"relationship\": \" Own - child\", \"race\": \" Black\", \"gender\": \" Male\", \"capital_gain\": 0, \"capital_loss\": 0, \"hours_per_week\": 40, \"native_country\": \" United - States\"}]}");
ProjectsResource.PredictRequest req = new ProjectsResource.PredictRequest(service, new GoogleCloudMlV1PredictRequest {
    HttpBody = new GoogleApiHttpBody {
    Data = JsonConvert.SerializeObject(toSend)
}, "projects/{project_name}/models/census/versions/v1");

GoogleApiHttpBody body = req.Execute();

After getting everything authenticated properly, I was getting a response back that had all null fields.

I checked my Google API and the requests were being registered as 200, so I couldn’t tell where my data way. After some investigation, I used the stream to read the actual response I found I was getting this response:

{"error": "<strong>Missing \"instances\" field in request body</strong>: {\"httpBody\":{\"data\":\"{\\\"instances\\\":[{\\\"age\\\":25,\\\"workclass\\\":\\\" Private\\\",\\\"education\\\":\\\" 11th\\\",\\\"education_num\\\":7,\\\"marital_status\\\":\\\" Never - married\\\",\\\"occupation\\\":\\\" Machine - op - inspct\\\",\\\"relationship\\\":\\\" Own - child\\\",\\\"race\\\":\\\" Black\\\",\\\"gender\\\":\\\" Male\\\",\\\"capital_gain\\\":0,\\\"capital_loss\\\":0,\\\"hours_per_week\\\":40,\\\"native_country\\\":\\\" United - States\\\"}]}\"}}"}

I can see clearly my “instances” object in the data, but it seems Google wants the value at the root of the body of the request. This means that GoogleApiHttpBody is serializing my request incorrectly.

GoogleCloudMlV1PredictRequest is not sending an acceptable “Body” for the Predict web service

To work around this, I used the CloudMachineLearningEngineService objects “Service” object to create my own request so I could set up the body of the request myself. I replaced the above code with the following, and ended up getting my predictions back for the given request as expected.

string credPath = @".\appkey.json";
var json = File.ReadAllText(credPath);
PersonalServiceAccountCred cr = JsonConvert.DeserializeObject(json);

// Create an explicit ServiceAccountCredential credential
var xCred = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(cr.ClientEmail)
            {
                Scopes = new [] {
                    CloudMachineLearningEngineService.Scope.CloudPlatform
                }
            }.FromPrivateKey(cr.PrivateKey));

            var service = new CloudMachineLearningEngineService(new BaseClientService.Initializer
            {
                HttpClientInitializer = xCred
            });

RootObject toSend = JsonConvert.DeserializeObject("{\"instances\": [{\"age\": 25, \"workclass\": \" Private\", \"education\": \" 11th\", \"education_num\": 7, \"marital_status\": \" Never - married\", \"occupation\": \" Machine - op - inspct\", \"relationship\": \" Own - child\", \"race\": \" Black\", \"gender\": \" Male\", \"capital_gain\": 0, \"capital_loss\": 0, \"hours_per_week\": 40, \"native_country\": \" United - States\"}]}");
ProjectsResource.PredictRequest req = new ProjectsResource.PredictRequest(service, new GoogleCloudMlV1PredictRequest
            {
                HttpBody = new GoogleApiHttpBody {
                    Data = JsonConvert.SerializeObject(toSend)
                }
            }, "projects/{project-name}/models/census/versions/v1");

string requestPath = req.Service.BaseUri + CloudMachineLearningEngineService.Version + "/" + req.Name + ":" + req.MethodName;
Task result = service.HttpClient.PostAsync(requestPath, new StringContent(JsonConvert.SerializeObject(toSend)));
Task.WaitAll(result);

HttpResponseMessage responseMessage = result.Result;

Task responseStreamTask = responseMessage.Content.ReadAsStringAsync();
Task.WaitAll(responseStreamTask);

string responseText = responseStreamTask.Result;

Leave a Reply