web-dev-qa-db-fra.com

MalformedJsonException avec Retrofit API?

J'ai besoin d'envoyer un json à mon webservice, json c'est: 

{
    "Sala": {
        "usuario": "%@",
        "adversario": "%@",
        "atualizacao": "%@",
        "device": "%@",
        "device_tipo": "ios"
    }
}

. J'essaie de le faire en utilisant Retrofit API 1.8 . Lorsque j'essaie d'envoyer, le message lève une exception.

Exception:

com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 7 path $

J'essaye ça

public class ChatObject {
    private String usuario;
    private String adversario;
    private String atualizacao;
    private String email;
    private String device;
    private String device_tipo;

Interface de modernisation

@POST("/WsChat/interacao.json")
    public void onReceiveMessage(@Body ChatObject obj,
                                 Callback<JsonElement> response);

Met en oeuvre

public void receiveMessage(){
    ///{\"Sala\":{\"usuario\":\"%@\",\"adversario\":\"%@\",\"atualizacao\":\"%@\",\"device\":\"%@\",\"device_tipo\":\"ios\"}}
    ChatObject chatObject = new ChatObject(BatalhaConfigs.USUARIO_EMAIL,
                                           BatalhaConfigs.ADVERSARIO_EMAIL,
                                           new Date().toString(),
                                           BatalhaConfigs.USUARIO_EMAIL,
                                           AndroidReturnId.getAndroidId(),
                                           "Android");

    RestAdapter adapter = new RestAdapter.Builder()
            .setLogLevel(RestAdapter.LogLevel.FULL)
            .setRequestInterceptor(new CustomRequestInterceptor())
            .setEndpoint(END_POINT)
            .build();
    ChatListener listener = adapter.create(ChatListener.class);
    listener.onReceiveMessage(chatObject, new Callback<JsonElement>() {
        @Override
        public void success(JsonElement jsonElement, retrofit.client.Response response) {
            Log.i("JSON ELEMENT->", jsonElement.toString());
        }

        @Override
        public void failure(RetrofitError error) {
            Log.i("FALHOU->", error.getLocalizedMessage());

        }
    });
}
16
FernandoPaiva

com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) est généralement lancé lorsqu'il existe un ou plusieurs caractères qui déforment le code JSON. Le message d’exception lui-même suggère de rendre la désérialisation plus tolérante. 

Mais je vous suggère de réparer votre JSON et de le supprimer des caractères indésirables.

Vous devez étendre GsonConverter et remplacer fromBody() pour que Gson soit lu à partir de la variable JsonReader tolérante. Ensuite, configurez-le sur RestAdapter. Cela tentera d'utiliser une variable JsonReader tolérante pour désérialiser, puis de la fermer, sinon une exception est levée. 

public class LenientGsonConverter extends GsonConverter {
private Gson mGson;

public LenientGsonConverter(Gson gson) {
    super(gson);
    mGson = gson;
}

public LenientGsonConverter(Gson gson, String charset) {
    super(gson, charset);
    mGson = gson;
}

@Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
    boolean willCloseStream = false; // try to close the stream, if there is no exception thrown using tolerant  JsonReader
    try {
        JsonReader jsonReader = new JsonReader(new InputStreamReader(body.in()));
        jsonReader.setLenient(true);
        Object o = mGson.fromJson(jsonReader,type);
        willCloseStream = true;
        return o;
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if(willCloseStream) {
            closeStream(body);
        }
    }

    return super.fromBody(body, type);
}

private void closeStream(TypedInput body){
        try {
            InputStream in = body.in();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

}

15
Nikola Despotoski

Semble sa légèrement modifié avec Retrofit 2.0

Voici comment je l'ai fait:

 Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://whatever.com")
        .addConverterFactory(LenientGsonConverterFactory.create(gson))
        .build();

Une nouvelle usine de gson indulgente:

public final class LenientGsonConverterFactory extends Converter.Factory {

        /**
         * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
         * decoding from JSON (when no charset is specified by a header) will use UTF-8.
         */
        public static LenientGsonConverterFactory create() {
            return create(new Gson());
        }

        /**
         * Create an instance using {@code gson} for conversion. Encoding to JSON and
         * decoding from JSON (when no charset is specified by a header) will use UTF-8.
         */
        public static LenientGsonConverterFactory create(Gson gson) {
            return new LenientGsonConverterFactory(gson);
        }

        private final Gson gson;

        private LenientGsonConverterFactory(Gson gson) {
            if (gson == null) throw new NullPointerException("gson == null");
            this.gson = gson;
        }

        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                                Retrofit retrofit) {
            TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
            return new LenientGsonResponseBodyConverter<>(gson, adapter);
        }

        @Override
        public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                              Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
            TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
            return new LenientGsonRequestBodyConverter<>(gson, adapter);
        }

    }

Analyse simplifiée des réponses:

    private class LenientGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
        private final Gson gson;
        private final TypeAdapter<T> adapter;

        LenientGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }

        @Override
        public T convert(ResponseBody value) throws IOException {
            JsonReader jsonReader = gson.newJsonReader(value.charStream());
            jsonReader.setLenient(true);
            try {
                return adapter.read(jsonReader);
            } finally {
                value.close();
            }
        }
    }

Création facile de requêtes:

   private class LenientGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
        private static final Charset UTF_8 = Charset.forName("UTF-8");

        private final Gson gson;
        private final TypeAdapter<T> adapter;

        LenientGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }

        @Override
        public RequestBody convert(T value) throws IOException {
            Buffer buffer = new Buffer();
            Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
            JsonWriter jsonWriter = gson.newJsonWriter(writer);
            jsonWriter.setLenient(true);
            adapter.write(jsonWriter, value);
            jsonWriter.close();
            return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
        }
    }

Je viens de copier le code source de Retrofit et d’ajouter une ligne à la demande et aux convertisseurs de réponse jsonWriter.setLenient(true);


Ou encore plus facile:

    Gson gson = new GsonBuilder()
        .setLenient()
        .create();

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://whatever.com")
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build(); 
9
Blundell
 RestAdapter adapterRfqPost = new RestAdapter.Builder()
                                    .setEndpoint(Constants.ENDPOINT)
                                            `enter code here`.setConverter(new ConstantsMethods.StringConverter())
                                    .build();

public static class StringConverter implements Converter {
        @Override
        public Object fromBody(TypedInput typedInput, Type type) throws ConversionException {
            String text = null;
            try {
                text = fromStream(typedInput.in());
            } catch (IOException ignored) {/*NOP*/ }
            return text;
        }

        @Override
        public TypedOutput toBody(Object o) {
            return null;
        }

        public static String fromStream(InputStream in) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            StringBuilder out = new StringBuilder();
            String newLine = System.getProperty("line.separator");
            String line;
            while ((line = reader.readLine()) != null) {
                out.append(line);
                out.append(newLine);
            }
            return out.toString();
        }
    }
1
amar mule

J'ai eu du mal un jour à trouver cette erreur et à faire ce que la "bonne réponse" de cette page disait, mais après tout, j'ai compris mon problème, c'était assigner la réponse d'un tableau qui était "int" (ma classe de modèle était aussi int) à un textView qui, bien sûr, m'a obligé à le convertir en chaîne de la valeur int. Je n'ai même pas eu besoin de faire la solution de @ Nikola Despotoski dans mon cas.

0
Hossein