如何解析 Android 中以换行符分隔的 Json 响应?
How to parse newline delimited Json response in Android?
NdJson
数据样本:
{"type":"data","id":"xyz"}
{"type":"value","id":"xcf"}
....
....
这是我的 Retrofit
和 RxJava
代码,可以很好地使用 limit=1
获取数据,即 {"type":"data","id":"xyz"}
.
adapter.create(API.class).getData()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<APIData>() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {}
@Override
public void onNext(APIData apidata) {}
});
我的模型class
模型class只有两个参数:
public class APIData(){
private String type;
private String id;
..// Getter and setter for above two fields
}
Api class
public interface WarehouseAPI {
@GET("/search?limit=1")
public Observable<APIData> getdata ();
}
将 @GET("/search?limit=1")
替换为 @GET("/search")
时出现的错误是 Malformed JSON: Syntax error
。
问题
如何正确解析 NdJson
?
有什么方法可以将响应存储在 List<APIData>
编辑-1
现在我正在尝试接受观察者中的通用 Response
:
adapter.create(APIData.class).getdata()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response>() {
@Override
public void onCompleted() {}
@Override
public void onNext(Response response) {}
});
但是得到 no suitable method found for subscribe(<anonymous Observer<Response>>)
编辑-2
但是,我犯了一些愚蠢的错误,我在 "Edit-1" 部分得到的错误现在已修复。现在我得到
Error:retrofit.RetrofitError: com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 2 column 2 path $
我假设服务器响应具有以下格式
{"type":"data","id":"xyz"}\n{"type":"data","id":"xyz"}
基本思想是接收来自服务器的响应作为字符串。将它拆分成数组,如 response.split("\n")
。遍历数组,为每个数组元素创建一个新的 json 对象。
正如您在评论中所描述的那样,我确实意识到这非常耗时。您还可以尝试使用 String replaceAll
方法将每一行转换为数组元素并解析整个字符串。喜欢
String myResponse = "[" + response.replaceAll("/\n/", ",") + "]";
Gson gson = new Gson();
MyEntity[] arr = gson.fromJson(myResponse, MyEntity[].class);
在改造的情况下。您将不得不使用自定义响应转换器。
我不会写完整的解决方案,因为您已经找到了使用自定义转换器完成它的方法。
为了让事情正常进行,我必须通过 Retrofit.Converter
添加 Custom converter
实现:
public class CustomConverter implements Converter {
@Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
String text = null;
try {
text = fromStream(body.in());
} catch (IOException e) {
e.printStackTrace();
}
return text;
}
@Override
public TypedOutput toBody(Object object) {
return null;
}
// Custom method to convert stream from request to string
public static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("\n");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}
}
而且效果很好!
首先,你的json不是Gson理解的json,所以你不能直接在Retrofit中使用Gson转换器。所以
public Observable<APIData> getdata ();
必须
public Observable<Response> getdata ();
那么,你有
adapter.create(WarehouseAPI.class).getdata()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map((Response res) -> new BufferedStreamReader(res.body.body().bytesStream()))
.flatMap(stream -> {
return Observable.create(subscriber -> {
String line;
while((line = stream.readLine() != null) {
subscriber.onNext(gson.fromJson(line, APIData.class));
}
subscriber.onCompleted();
});
});
您可以订阅它并接收您的 json 列表中的每个项目。
这没有经过测试,也没有处理错误情况(简而言之,订阅的有效性也没有经过测试)。
虽然它应该主要是在正确的方向。
NdJson
数据样本:
{"type":"data","id":"xyz"}
{"type":"value","id":"xcf"}
....
....
这是我的 Retrofit
和 RxJava
代码,可以很好地使用 limit=1
获取数据,即 {"type":"data","id":"xyz"}
.
adapter.create(API.class).getData()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<APIData>() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {}
@Override
public void onNext(APIData apidata) {}
});
我的模型class
模型class只有两个参数:
public class APIData(){
private String type;
private String id;
..// Getter and setter for above two fields
}
Api class
public interface WarehouseAPI {
@GET("/search?limit=1")
public Observable<APIData> getdata ();
}
将 @GET("/search?limit=1")
替换为 @GET("/search")
时出现的错误是 Malformed JSON: Syntax error
。
问题
如何正确解析 NdJson
?
有什么方法可以将响应存储在 List<APIData>
编辑-1
现在我正在尝试接受观察者中的通用 Response
:
adapter.create(APIData.class).getdata()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Response>() {
@Override
public void onCompleted() {}
@Override
public void onNext(Response response) {}
});
但是得到 no suitable method found for subscribe(<anonymous Observer<Response>>)
编辑-2
但是,我犯了一些愚蠢的错误,我在 "Edit-1" 部分得到的错误现在已修复。现在我得到
Error:retrofit.RetrofitError: com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 2 column 2 path $
我假设服务器响应具有以下格式
{"type":"data","id":"xyz"}\n{"type":"data","id":"xyz"}
基本思想是接收来自服务器的响应作为字符串。将它拆分成数组,如 response.split("\n")
。遍历数组,为每个数组元素创建一个新的 json 对象。
正如您在评论中所描述的那样,我确实意识到这非常耗时。您还可以尝试使用 String replaceAll
方法将每一行转换为数组元素并解析整个字符串。喜欢
String myResponse = "[" + response.replaceAll("/\n/", ",") + "]";
Gson gson = new Gson();
MyEntity[] arr = gson.fromJson(myResponse, MyEntity[].class);
在改造的情况下。您将不得不使用自定义响应转换器。 我不会写完整的解决方案,因为您已经找到了使用自定义转换器完成它的方法。
为了让事情正常进行,我必须通过 Retrofit.Converter
添加 Custom converter
实现:
public class CustomConverter implements Converter {
@Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
String text = null;
try {
text = fromStream(body.in());
} catch (IOException e) {
e.printStackTrace();
}
return text;
}
@Override
public TypedOutput toBody(Object object) {
return null;
}
// Custom method to convert stream from request to string
public static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("\n");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}
}
而且效果很好!
首先,你的json不是Gson理解的json,所以你不能直接在Retrofit中使用Gson转换器。所以
public Observable<APIData> getdata ();
必须
public Observable<Response> getdata ();
那么,你有
adapter.create(WarehouseAPI.class).getdata()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map((Response res) -> new BufferedStreamReader(res.body.body().bytesStream()))
.flatMap(stream -> {
return Observable.create(subscriber -> {
String line;
while((line = stream.readLine() != null) {
subscriber.onNext(gson.fromJson(line, APIData.class));
}
subscriber.onCompleted();
});
});
您可以订阅它并接收您的 json 列表中的每个项目。
这没有经过测试,也没有处理错误情况(简而言之,订阅的有效性也没有经过测试)。
虽然它应该主要是在正确的方向。