0
votes

I'm trying to use the Google Machine Learning API and I'm facing two problems. In the API explorer I put the correct information and I get a response error:

Code 200 "error": "Missing \"instances\" field in request body: {\n \"httpBody\": \n
{\n \"data\": \"\\"instances\\" : \\"teste\\"\",\n
\"contentType\": \"application/json\"\n }\n}"

The request find my model (if I change the value in field name I get another error) but don't understand my json. That's the json:

{"instances" : [{"key":"0", "image_bytes": {"b64": "mybase64"} }]}

When I do the predict on the command line using gcloud, I get no errors and everything seems ok. The Json that I was create for gcloud is a little bit different:

{"key":"0", "image_bytes": {"b64": "mybase64"} }

I already tryied that one in the API explorer and no success.

So, I decided to use the .Net Api to try the predict and I get other situation: The Response is Empty (???).

Here is my code:

   'get the service credential that I created
    Dim credential = Await GetCredential()


    Dim myService As New CloudMachineLearningEngineService(New BaseClientService.Initializer() With {
                                                                    .ApplicationName = "my Project Name (Is That It???)",
                                                                    .ApiKey = "my API Key",
                                                                    .HttpClientInitializer = credential
                                                               })

    Dim myBase64 As String = GetBase64("my image path to convert into a base64 String")
    Dim myJsonRequest As String = "{""instances"" : [{""key"":""0"", ""image_bytes"": {""b64"": """ + myBase64 + """}}]}"

    Dim myRequest = New GoogleCloudMlV1PredictRequest With {
        .HttpBody = New GoogleApiHttpBody With {.Data = myJsonRequest,
                                                .ContentType = "application/json"
                                                }
    }

    'If I change the model name I get error
    Dim myPredictRequest = myService.Projects.Predict(myRequest, "projects/myProject/models/myModel/versions/v1")
    myPredictRequest.AccessToken = credential.Token.AccessToken
    myPredictRequest.OauthToken = credential.Token.AccessToken
    myPredictRequest.Key = "my API Key

    'Execute the request
    Dim myResponse = myPredictRequest.Execute()

    'at this point, myResponse is Empty (myResponse.ContentType Is Nothing, myResponse.Data Is Nothing And myResponse.ETag Is Nothing)

If I change the model name I get a error informing that my model was not found, so my credentials are right.

I don't know what I'm doing wrong. Someboby can help with any of this issues?

Thanks!

UPDATE: --------------------------

I changed this Execute Command: Dim myResponse = myPredictRequest.Execute()
To This One: Dim s = StreamToString(myPredictRequest.ExecuteAsStream())

and Now I can get the same error with .Net API and google developers interface (Missing instances field...). So If someboby just Know what is wrong with my Json request, It will help a lot.

3

3 Answers

0
votes

The JSON you put in the API explorer is indeed correct (assuming, of course, your model has inputs key and image_bytes). This appears to be a bug with the explorer I will report.

The reason you are getting the error you are in the .NET code is because you are using an .HttpBody field. This code:

Dim myJsonRequest As String = "{""instances"" : [{""key"":""0"", ""image_bytes"": {""b64"": """ + myBase64 + """}}]}"
Dim myRequest = New GoogleCloudMlV1PredictRequest With {
    .HttpBody = New GoogleApiHttpBody With {.Data = myJsonRequest,
                                            .ContentType = "application/json"
                                            }
}

Will produce a JSON request that looks like this:

{
  "httpBody": {
    "data": "{\"instances\" : [{\"key\":\"0\", \"image_bytes\": {\"b64\": \"mybase64\"} }]}",
    "contentType": "application\/json"
  }
}

When what you really need is:

{"instances" : [{"key":"0", "image_bytes": {"b64": "mybase64"} }]}

Hence the error message you see.

I don't know how to generate the correct response using the .NET library; based on the Python example in the docs, I would guess:

Dim myJsonRequest As String = "{""instances"" : [{""key"":""0"", ""image_bytes"": {""b64"": """ + myBase64 + """}}]}"
Dim myPredictRequest = myService.Projects.Predict(myJsonRequest, "projects/myProject/models/myModel/versions/v1")

But I don't have a good way of testing that. For reference, the Python equivalent is:

response = service.projects().predict(
    name=name,
    body=myJsonRequest
).execute()
0
votes

I solved the problem with .Net API. I created two new classes Inherits the Google API's classes. Something like that:

Imports Google.Apis.CloudMachineLearningEngine.v1.Data
Imports Newtonsoft.Json

Public Class myGoogleCloudMlV1PredictRequest
    Inherits GoogleCloudMlV1PredictRequest

    <JsonProperty("instances")>
    Public Property MyHttpBody As List(Of myGoogleApiHttpBody)
End Class



Imports Google.Apis.CloudMachineLearningEngine.v1.Data
Imports Newtonsoft.Json

Public Class myGoogleApiHttpBody
    Inherits GoogleApiHttpBody

    <JsonProperty("image_bytes")>
    Public Property MyData As image_byte

    <JsonProperty("key")>
    Public Property key As String

End Class

So, in my original code I change this part:

    Dim myBase64 As String = GetBase64("my_image_path_to_convert_into_a _base64_String")
    Dim myJsonRequest As String = "{""instances"" : [{""key"":""0"", ""image_bytes"": {""b64"": """ + myBase64 + """}}]}"

    Dim myRequest = New GoogleCloudMlV1PredictRequest With {
        .HttpBody = New GoogleApiHttpBody With {.Data = myJsonRequest,
                                               .ContentType = "application/json"
                                            }
    }

For this one:

Dim myBase64 As String = GetBase64("my_image_path_to_convert_into_a _base64_String")
Dim myRequest = New myGoogleCloudMlV1PredictRequest With {
            .MyHttpBody = New List(Of myGoogleApiHttpBody)()
        }

        Dim item As myGoogleApiHttpBody = New myGoogleApiHttpBody With {
                                                    .key = "0",
                                                    .MyData = New image_byte With {
                                                        .b64 = myBase64
                                                    }
                                                 }
        myRequest.MyHttpBody.Add(item)

And voilá, It's working!

Thanks for everyone!!

0
votes

Github issue #1068 shows two work-arounds for this problem.

In summary, use service.ModifyRequest to insert the raw JSON content.

Or use service.HttpClient.PostAsync(...) directly.