2014-10-10 21 views
5

Mam zewnętrzny serwis internetowy, który w organizmie odpowiedzi zwraca json ale zagnieżdżone w nawiasach tak:Retrofit - usuwanie nieprawidłowe znaki z ciałem odpowiedzi przed parsowania go jako JSON

({"door_x":"103994.001461","door_y":"98780.7862376", "distance":"53.3"}) 

Stosując ten kod:

class AddressInfo { 
    String door_x; 
    String door_y; 
} 

interface AddressWebService { 
    @GET("/reversegeocoding") 
    AddressInfo reverseGeocoding(@Query("x") double x, @Query("y") double y); 
} 

Oczywiście nie. To jest stacktrace:

retrofit.RetrofitError: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 
     at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:377) 
     at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240) 
     at com.something.$Proxy7.reverseGeocoding(Native Method) 
     at com.something.ReverseGeocodingService.getAddress(ReverseGeocodingService.java:24) 
     at com.something.LocationProvider$1.run(LocationProvider.java:77) 
     at java.lang.Thread.run(Thread.java:864) 
Caused by: retrofit.converter.ConversionException: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 
     at retrofit.converter.GsonConverter.fromBody(GsonConverter.java:67) 
     at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:362) 
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240) 
            at com.something.$Proxy7.reverseGeocoding(Native Method) 
            at com.something.ReverseGeocodingService.getAddress(ReverseGeocodingService.java:24) 
            at com.something.LocationProvider$1.run(LocationProvider.java:77) 
            at java.lang.Thread.run(Thread.java:864) 
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 
     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176) 
     at com.google.gson.Gson.fromJson(Gson.java:803) 
     at com.google.gson.Gson.fromJson(Gson.java:768) 
     at retrofit.converter.GsonConverter.fromBody(GsonConverter.java:63) 
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:362) 
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240) 
            at com.something.$Proxy7.reverseGeocoding(Native Method) 
            at com.something.ReverseGeocodingService.getAddress(ReverseGeocodingService.java:24) 
            at com.something.LocationProvider$1.run(LocationProvider.java:77) 
            at java.lang.Thread.run(Thread.java:864) 
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 
     at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:374) 
     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:165) 
            at com.google.gson.Gson.fromJson(Gson.java:803) 
            at com.google.gson.Gson.fromJson(Gson.java:768) 
            at retrofit.converter.GsonConverter.fromBody(GsonConverter.java:63) 
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:362) 
            at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240) 
            at com.something.$Proxy7.reverseGeocoding(Native Method) 
            at com.something.ReverseGeocodingService.getAddress(ReverseGeocodingService.java:24) 
            at com.something.LocationProvider$1.run(LocationProvider.java:77) 
            at java.lang.Thread.run(Thread.java:864) 

Jaki jest najlepszy sposób na usunięcie nawiasów przed analizą json?

+0

Najwyraźniej serwer jest serwerem obsługującym JSONP, ale nie mam sposobu na uniknięcie zawijania nawiasów. – pomber

Odpowiedz

11

Możesz bezboleśnie oczyszczać odpowiedź w swoim GsonConverter przed przekształceniem obiektu w obiekt typu.

public class CleanGsonConverter extends GsonConverter{ 

      public CleanGsonConverter(Gson gson) { 
       super(gson); 
      } 

      public CleanGsonConverter(Gson gson, String encoding) { 
       super(gson, encoding); 
      } 

      @Override 
      public Object fromBody(TypedInput body, Type type) throws ConversionException { 
       String dirty = toString(body); 
       String clean = dirty.replaceAll("(^\\(|\\)$)", ""); 
       body = new JsonTypedInput(clean.getBytes(Charset.forName(HTTP.UTF_8))); 
       return super.fromBody(body, type); 
      } 
      private String toString(TypedInput body){ 
        BufferedReader br = null; 
        StringBuilder sb = new StringBuilder(); 
        String line; 
        try { 
         br = new BufferedReader(new InputStreamReader(body.in())); 
         while ((line = br.readLine()) != null) { 
          sb.append(line); 
         } 

        } catch (IOException e) { 
         e.printStackTrace(); 
        } finally { 
         if (br != null) { 
          try { 
           br.close(); 
          } catch (IOException e) { 
           e.printStackTrace(); 
          } 
         } 
        } 

        return sb.toString(); 

       } 
     }; 

JsonTypedInput:

public class JsonTypedInput implements TypedInput{ 

     private final byte[] mStringBytes; 

     JsonTypedInput(byte[] stringBytes) { 
      this.mStringBytes = stringBytes; 
     } 


     @Override 
     public String mimeType() { 
      return "application/json; charset=UTF-8"; 
     } 



     @Override 
     public long length() { 
      return mStringBytes.length; 
     } 

     @Override 
     public InputStream in() throws IOException { 
      return new ByteArrayInputStream(mStringBytes); 
     } 
    } 

Tu podklasy GsonConverter aby uzyskać dostęp do odpowiedzi zanim zostanie on przekształcony do obiektu. JsonTypedOutput służy do zachowania typu odpowiedzi MIME po wyczyszczeniu go ze znaków-śmieci.

Zastosowanie:

restAdapterBuilder.setConverter(new CleanGsonConverter(gson));

Winić go na swoich chłopaków wspierających. :)

+0

Miło, nie miałem jeszcze okazji go przetestować. Tylko jedno pytanie, jak powinienem zainicjować parametr gson przekazany do konstruktora CleanGsonConverter? – pomber

+0

Tak samo, jak w przypadku GsonConverter. 'new CleanGsonConverter (gsonBuilder.create())' –

+0

Dzięki! to działa. Jedyne, co musiałem zmienić, to wyrażenie regularne, ponieważ oryginał zastępował cały tekst. Użyłem "(^ \\ (| \\) $)" – pomber

1

alternatywna regex przekonwertować z jsonp (dirty) do formatu JSON (czystej):

String clean = dirty.replaceFirst("(?s)^\\((.*)\\)$", "$1");
+0

to działa podczas korzystania z usługi SOAP, dzięki – AbuQauod

5

rozwiązanie dla Retrofit 2

Poniższy kod jest taki sam jak GsonConverter oprócz Ciebie może edytować Response przed konwersją do swojego modelu

Edit public T convert(ResponseBody value) do czyszczenia Response

/** 
* Modified by TarekkMA on 8/2/2016. 
*/ 

public class MyJsonConverter extends Converter.Factory { 

    public static MyJsonConverter create() { 
     return create(new Gson()); 
    } 

    public static JsonHandler create(Gson gson) { 
     return new JsonHandler(gson); 
    } 

    private final Gson gson; 

    private JsonHandler(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 GsonResponseBodyConverter<>(gson, adapter); 
    } 

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


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

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

     GsonRequestBodyConverter(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); 
      adapter.write(jsonWriter, value); 
      jsonWriter.close(); 
      return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); 
     } 
    } 

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

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

     @Override 
     public T convert(ResponseBody value) throws IOException { 
      String dirty = value.string(); 
      String clean = dirty.replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + 
        "<string xmlns=\"http://tempuri.org/\">","").replace("</string>",""); 
      try { 
       return adapter.fromJson(clean); 
      } finally { 
       value.close(); 
      } 
     } 
    } 


} 
  • Innym rozwiązaniem jest obwinianie niedoświadczonego programisty backendu.
+0

Masz kilka literówek: musisz zastąpić "JsonHandler" z "MyJsonConverter" lub na odwrót. – George

+0

lol @ 'obwiniaj niedoświadczonego programistę backendu :) –