0

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.

rhaertel80
  • 8,254
  • 1
  • 31
  • 47
RMH
  • 222
  • 1
  • 11

3 Answers3

0

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()
rhaertel80
  • 8,254
  • 1
  • 31
  • 47
  • Thanks @rhaertel80. Now I understand where is the problem. In API explore, if I choose the FreeForm Editor in Request Body Field I can put the correct Json request and everything works fine (I didn't know this option). The problem in API explore is the Structered Editor (It's default) and with this option you can't put the correct json. It's the same on .Net API, you can't construct a object with a free structure where you can put your own json. I installed python and try it just to know if I can call the API, Everything works with Python API too. Thanks! – RMH May 06 '17 at 13:31
  • Glad to hear you got some things working. We'll work on getting a good story for .NET, but it may take some time. – rhaertel80 May 06 '17 at 23:25
0

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!!

RMH
  • 222
  • 1
  • 11
0

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.

Chris
  • 1,685
  • 10
  • 15