我有一个外部Web服务,在响应主体中返回json但嵌套在括号中,如下所示:
({"door_x":"103994.001461","door_y":"98780.7862376", "distance":"53.3"})
使用此代码:
class AddressInfo {
String door_x;
String door_y;
}
interface AddressWebService {
@GET("/reversegeocoding")
AddressInfo reverseGeocoding(@Query("x") double x, @Query("y") double y);
}
显然失败了。这是堆栈跟踪:
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)
在解析json之前删除括号的最佳方法是什么?
答案 0 :(得分:12)
您可以在GsonConverter
Gson
之前轻松地清除 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();
}
};
在将身体反序列化为类型对象之前的响应。
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);
}
}
JsonTypedInput:
GsonConverter
这里我将JsonTypedOutput
子类化,以便在转换为对象之前访问响应。 restAdapterBuilder.setConverter(new CleanGsonConverter(gson));
用于在从垃圾字符中清除响应后保留响应的mime类型。
用法:
{{1}}
归咎于你的后端人员。 :)
答案 1 :(得分:7)
以下代码与GsonConverter
相同,但您可以在转换为模型之前编辑Response
修改 public T convert(ResponseBody value)
以清除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();
}
}
}
}
答案 2 :(得分:1)
将jsonp(脏)转换为json(clean)的替代正则表达式:
String clean = dirty.replaceFirst("(?s)^\\((.*)\\)$", "$1");
答案 3 :(得分:0)
最初回答here
要解析无效的JSON或String或JSONP响应,请使用ScalarConverterFactory。
要解析JSON响应,请使用 GsonConverterFactory 。
如果您使用say flatMap调用JSON API,然后再使用JSONP API,则同时使用GsonConverterFactory( JSON所需)和ScalarConverterFactory( JSONP所需)。
确保gradle中具有以下依赖项:
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
//For serialising JSONP add converter-scalars
implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
//An Adapter for adapting RxJava 2.x types.
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
添加converterFactories进行改造,并在构建Gson时使用setLenient()摆脱错误JSON document was not fully consumed.
val gson = GsonBuilder()
.setLenient()
.create()
val retrofit = Retrofit.Builder()
.baseUrl("http://api.flickr.com/")
.client(builder.build())
.addConverterFactory(ScalarsConverterFactory.create()) //important
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
@GET("end-point/to/some/jsonp/url")
fun getJsonpData() : Observable<String>
使用转换器通过删除存在的前缀和后缀将无效的JSON转换为JSON。 然后通过
将字符串转换为您的数据模型SomeDataModel model = Gson().fromJson<SomeDataModel>(jsonResponse,
SomeDataModel::class.java)