5

I am trying to translate some text by using Microsoft translator API. I am using Retrofit 2. This is the code:

 public RestClient() {
    final OkHttpClient httpClient = new OkHttpClient.Builder()
            .addNetworkInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    final Request originalRequest = chain.request();
                    Request newRequest;

                    newRequest = originalRequest.newBuilder()
                            .header("Content-Type", "application/json")
                            .header("Ocp-Apim-Subscription-Key", "KEY")
                            .header("X-ClientTraceId", java.util.UUID.randomUUID().toString())
                            .build();
                    return chain.proceed(newRequest);
                }
            })
            .addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            .build();


    // Build the retrofit config from our http client
    final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.cognitive.microsofttranslator.com/")
            .client(httpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    // Build api instance from retrofit config
    api = retrofit.create(RestApi.class);
}


public interface RestApi {

    @POST("translate?api-version=3.0&from=en&to=zh-Latn")
    Call<TranslationResultDTO> getTranslation(@Body final RequestBody Text);
}


 public void getTranslation(final String text, final RestCallback<TranslationResultDTO> translationResultCallback) {
    final JsonObject jsonBody = new JsonObject();
    jsonBody.addProperty("Text", text);
    RequestBody textToTranslateBody = RequestBody.create(MediaType.parse("application/json"), jsonBody.toString());

    Call<TranslationResultDTO> call = api.getTranslation(textToTranslateBody);

    call.enqueue(new Callback<TranslationResultDTO>() {
        @Override
        public void onResponse(Call<TranslationResultDTO> call, retrofit2.Response<TranslationResultDTO> response) {
            final int responseCode = response.code();
            ....
        }

        @Override
        public void onFailure(Call<TranslationResultDTO> call, Throwable t) {
            ....
        }
    });
}

I get an error from the server. The error says that the body in not a valid JSON.

Does someone know where is the problem?? Thanks in advance!

UPDATE

Here is the code for another solution I have tried as well. This solution is using a POJO class:

public class Data {

@SerializedName("Text")
private String text;

public Data(String text) {
    this.text = text;
}

public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}

}

@POST("translate?api-version=3.0&from=en&to=zh-Latn")
Call<TranslationResultDTO> getTranslation(@Body final Data Text);


 Data data = new Data("text value to translate");
    Call<TranslationResultDTO> call = api.getTranslation(data);

Also the same error :/

IrApp
  • 1,823
  • 5
  • 24
  • 42

2 Answers2

3

The error says that the body in not a valid JSON.

It was an expected response from server. Let's say below is your JSON body that you want to send,

{
    "key_1" : "value 1",
    "key_2" : "value 2",
}

When you use JsonObject#toString(), it'll become like this

{\"key_1\" : \"value 1\", \"key_2\" : \"value 2\"}

If you send above JSON data/body to the server, it'll be treated as normal String by the server.

Still you need to use toString() here because MediaType#parse() doesn't accept JsonObject parameters.

What's the solution?

As Ishan Fernando mentioned in his comment you need to create a custom POJO class to prepare the JSON body for the request. Or use HashMap to prepare the body.

Create POJO like below

import com.google.gson.annotations.SerializedName;

public class Data {

    @SerializedName("Text")
    private String text;

    public Data(String text) {
        this.text = text;
    }

    // getter and setter methods
}

And use it

Data data = new Data("text value to translate"); // construct object
Call<TranslationResultDTO> call = api.getTranslation(data); // change method parameter

And also adjust the method parameter in API interface

Call<TranslationResultDTO> getTranslation(@Body final Data text); // change method parameter

Edit 1

I went through the documentation attached to your question. I made a little mistake. JSON body should contain JSONArray, not JSONObject. Like below

[
    {
        "Text" : "text to translate"
    }
]

Change method parameter to List<Data> in the API interface

Call<TranslationResultDTO> getTranslation(@Body final List<Data> text); // change method parameter

Use it like below

Data data = new Data("text value to translate"); // construct object
List<Data> objList = new ArrayList<>();
objList.add(data);
Call<TranslationResultDTO> call = api.getTranslation(objList);  // change method parameter

Edit 2

And also API will respond with JSONArray. Sample response

[
    {
        "detectedLanguage": {
        "language": "en",
        "score": 1.0
        },
        "translations": [
        {
            "text": "Hallo Welt!",
            "to": "de"
        },
        {
            "text": "Salve, mondo!",
            "to": "it"
            }
        ]
    }
]

Create below POJO classes to parse JSON response properly

Translation.java

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class Translation {

    @SerializedName("text")
    @Expose
    private String text;
    @SerializedName("to")
    @Expose
    private String to;

    // constructors, getter and setter methods

}

DetectedLanguage.java

import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class DetectedLanguage {

    @SerializedName("language")
    @Expose
    private String language;
    @SerializedName("score")
    @Expose
    private float score;

    // constructors, getter and setter methods

}

And finally, adjust TranslationResultDTO.java class like below.

import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

public class TranslationResultDTO {

    @SerializedName("detectedLanguage")
    @Expose
    private DetectedLanguage detectedLanguage;
    @SerializedName("translations")
    @Expose
    private List<Translation> translations = null;

    // constructors, getter and setter methods

}

Interface class

Call<List<TranslationResultDTO>> call = api.getTranslation(objList);    // change method parameter
Shashanth
  • 4,995
  • 7
  • 41
  • 51
  • I have tried as well, and also not working. I will update the question to add the code for this solution – IrApp Sep 05 '18 at 10:17
  • Then I get this error: "java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $" – IrApp Sep 05 '18 at 10:45
  • 1
    I updated my answer once again. Sorry for the inconvenience – Shashanth Sep 05 '18 at 10:55
1

If you see the method right, u need to pass JsonObject:

Call<TranslationResultDTO> getTranslation(@Body final JsonObject Text)

But instead you are passing "textToTranslateBody" which is of type RequestBody not JsonObject. You have to pass "jsonBody" which is of type JsonObject.

Prashanth Verma
  • 588
  • 3
  • 11
  • Sorry, it is a copy/paste error! I have already try that and it is not working :/. My question is updated! – IrApp Sep 05 '18 at 09:03
  • Okay..got it..!! – Prashanth Verma Sep 05 '18 at 09:06
  • I dont get one thing, why are you not sending in JsonObject, why do you want to send "RequestBody" type?. Is "RequestBody" a POJO?. If it is a POJO then convert the POJO to JsonObject and pass the JsonObject – Prashanth Verma Sep 05 '18 at 09:10
  • RequestBody is a class from `okhttp3`. I have try also with JsonObject, but booth of them are not working – IrApp Sep 05 '18 at 09:11
  • Check the solution here: https://stackoverflow.com/questions/21398598/how-to-post-raw-whole-json-in-the-body-of-a-retrofit-request – IrApp Sep 05 '18 at 09:15
  • 1
    Did you try to create a POJO class and pass that? Otherwise you can add those values to Hashmap and try. – Ishan Fernando Sep 05 '18 at 09:17
  • **Note**: `JsonObject` is from `com.google.gson` package and `JSONObject` is from `org.json` package. Be sure to use proper import statement. – Shashanth Sep 05 '18 at 09:24