개조를 사용하여 GSON을 사용하여 중첩된 JSON 개체 가져오기
Android 앱에서 API를 사용하고 있는데 JSON의 반응은 모두 다음과 같습니다.
{
'status': 'OK',
'reason': 'Everything was fine',
'content': {
< some data here >
}
에 'POJO'가 입니다.status
,reason
[ ] 및 [ 。content
POJO라고 합니다.
Gson을 추출할 수 있는 ?content
POJO는?
내장된 개체를 반환하는 사용자 지정 역직렬화기를 작성합니다.
예를 들어 JSON은 다음과 같습니다.
{
"status":"OK",
"reason":"some reason",
"content" :
{
"foo": 123,
"bar": "some value"
}
}
★★★★★★★★★★★★★★★★★★★★★★★★★.Content
POJO:
class Content
{
public int foo;
public String bar;
}
다음으로 디시리얼라이저를 작성합니다.
class MyDeserializer implements JsonDeserializer<Content>
{
@Override
public Content deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, Content.class);
}
}
, 이제 ,Gson
GsonBuilder
이치노
Gson gson =
new GsonBuilder()
.registerTypeAdapter(Content.class, new MyDeserializer())
.create();
수 .Content
:
Content c = gson.fromJson(myJson, Content.class);
댓글에서 추가할 편집:
메시지 유형은 다르지만 모두 "content" 필드가 있는 경우 다음과 같이 Deserializer를 일반화할 수 있습니다.
class MyDeserializer<T> implements JsonDeserializer<T>
{
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, type);
}
}
각 유형의 인스턴스를 등록하기만 하면 됩니다.
Gson gson =
new GsonBuilder()
.registerTypeAdapter(Content.class, new MyDeserializer<Content>())
.registerTypeAdapter(DiffContent.class, new MyDeserializer<DiffContent>())
.create();
했을 때.fromJson()
타입은 디시리얼라이저로 이동하기 때문에 모든 타입에서 동작합니다.
마지막으로 Retrofit 인스턴스를 작성할 때:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
@BrianRoach b @ @ @ @ @ @ @ @ 。 다 커스텀이 커스텀 .TypeAdapter
은 꼭 하셔야 합니다.TypeAdapter
GSON의 새로운 인스턴스, 그렇지 않으면 두 번째 인스턴스로TypeAdapter
이것은 가 새로운 있기 때문입니다.Gson
커스텀 디시리얼라이저 내의 인스턴스.
예를 들어 다음과 같은 json이 있는 경우:
{
"status": "OK",
"reason": "some reason",
"content": {
"foo": 123,
"bar": "some value",
"subcontent": {
"useless": "field",
"data": {
"baz": "values"
}
}
}
}
그리고 이 JSON을 다음 오브젝트에 매핑하려고 했습니다.
class MainContent
{
public int foo;
public String bar;
public SubContent subcontent;
}
class SubContent
{
public String baz;
}
합니다.SubContent
의 »TypeAdapter
할 수 보다 견고하게 하기 위해 다음을 수행할 수 있습니다.
public class MyDeserializer<T> implements JsonDeserializer<T> {
private final Class mNestedClazz;
private final Object mNestedDeserializer;
public MyDeserializer(Class nestedClazz, Object nestedDeserializer) {
mNestedClazz = nestedClazz;
mNestedDeserializer = nestedDeserializer;
}
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
GsonBuilder builder = new GsonBuilder();
if (mNestedClazz != null && mNestedDeserializer != null) {
builder.registerTypeAdapter(mNestedClazz, mNestedDeserializer);
}
return builder.create().fromJson(content, type);
}
}
다음과 같이 작성합니다.
MyDeserializer<Content> myDeserializer = new MyDeserializer<Content>(SubContent.class,
new SubContentDeserializer());
Gson gson = new GsonBuilder().registerTypeAdapter(Content.class, myDeserializer).create();
은 단순히 '콘텐츠'의 네스트된 '에도 쉽게 할 수 .MyDeserializer
값을 합니다.
조금 늦었지만 이게 누군가를 도울 수 있기를 바란다.
다음의 타입 어댑터 팩토리를 작성하기만 하면 됩니다.
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("content")) {
jsonElement = jsonObject.get("content");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
GSON 빌더에 추가합니다.
.registerTypeAdapterFactory(new ItemTypeAdapterFactory());
또는
yourGsonBuilder.registerTypeAdapterFactory(new ItemTypeAdapterFactory());
며칠 전에도 같은 문제가 있었어응답 래퍼 클래스와 RxJava 트랜스포머를 사용하여 이 문제를 해결했습니다.이것은 매우 유연한 솔루션이라고 생각합니다.
래퍼:
public class ApiResponse<T> {
public String status;
public String reason;
public T content;
}
상태가 정상이 아닌 경우 던지는 사용자 지정 예외:
public class ApiException extends RuntimeException {
private final String reason;
public ApiException(String reason) {
this.reason = reason;
}
public String getReason() {
return apiError;
}
}
Rx 변압기:
protected <T> Observable.Transformer<ApiResponse<T>, T> applySchedulersAndExtractData() {
return observable -> observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(tApiResponse -> {
if (!tApiResponse.status.equals("OK"))
throw new ApiException(tApiResponse.reason);
else
return tApiResponse.content;
});
}
사용 예:
// Call definition:
@GET("/api/getMyPojo")
Observable<ApiResponse<MyPojo>> getConfig();
// Call invoke:
webservice.getMyPojo()
.compose(applySchedulersAndExtractData())
.subscribe(this::handleSuccess, this::handleError);
private void handleSuccess(MyPojo mypojo) {
// handle success
}
private void handleError(Throwable t) {
getView().showSnackbar( ((ApiException) throwable).getReason() );
}
주제: RxJava - Gson - "글로벌" 역직렬화, 응답 유형 변경
Brian의 생각을 이어가자면, 거의 항상 많은 REST 리소스가 각각 독자적인 루트를 가지고 있기 때문에, 디시리얼라이제이션의 일반화에 도움이 될 수 있습니다.
class RestDeserializer<T> implements JsonDeserializer<T> {
private Class<T> mClass;
private String mKey;
public RestDeserializer(Class<T> targetClass, String key) {
mClass = targetClass;
mKey = key;
}
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonElement content = je.getAsJsonObject().get(mKey);
return new Gson().fromJson(content, mClass);
}
}
다음으로 위에서 샘플 payload를 해석하기 위해 GSON 디시리얼라이저를 등록할 수 있습니다.
Gson gson = new GsonBuilder()
.registerTypeAdapter(Content.class, new RestDeserializer<>(Content.class, "content"))
.build();
더 나은 해결책은 이것일 수 있습니다.
public class ApiResponse<T> {
public T data;
public String status;
public String reason;
}
그리고 서비스를 다음과 같이 정의합니다.
Observable<ApiResponse<YourClass>> updateDevice(..);
제 경우, 응답마다 "콘텐츠" 키가 바뀝니다.예:
// Root is hotel
{
status : "ok",
statusCode : 200,
hotels : [{
name : "Taj Palace",
location : {
lat : 12
lng : 77
}
}, {
name : "Plaza",
location : {
lat : 12
lng : 77
}
}]
}
//Root is city
{
status : "ok",
statusCode : 200,
city : {
name : "Vegas",
location : {
lat : 12
lng : 77
}
}
이 경우 위와 같은 솔루션을 사용했지만 수정해야 했습니다.여기서 요점을 볼 수 있습니다.여기 SOF에 올리기에는 좀 너무 커요.
주석@InnerKey("content")
나머지 코드는 Gson에서의 사용을 용이하게 하기 위한 것입니다.
이는 @AYarulin과 동일한 솔루션이지만 클래스 이름이 JSON 키 이름이라고 가정합니다.이 방법에서는 클래스 이름만 전달하면 됩니다.
class RestDeserializer<T> implements JsonDeserializer<T> {
private Class<T> mClass;
private String mKey;
public RestDeserializer(Class<T> targetClass) {
mClass = targetClass;
mKey = mClass.getSimpleName();
}
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonElement content = je.getAsJsonObject().get(mKey);
return new Gson().fromJson(content, mClass);
}
}
위에서 샘플 payload를 해석하려면 GSON 디시리얼라이저를 등록합니다.이는 키가 대소문자를 구분하기 때문에 문제가 됩니다.따라서 클래스 이름의 대소문자는 JSON 키의 대소문자와 일치해야 합니다.
Gson gson = new GsonBuilder()
.registerTypeAdapter(Content.class, new RestDeserializer<>(Content.class))
.build();
Brian Roach와 AYarulin의 답변을 바탕으로 한 Kotlin 버전입니다.
class RestDeserializer<T>(targetClass: Class<T>, key: String?) : JsonDeserializer<T> {
val targetClass = targetClass
val key = key
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): T {
val data = json!!.asJsonObject.get(key ?: "")
return Gson().fromJson(data, targetClass)
}
}
@Brian Roach와 @rafakob i의 답변에 따르면 다음과 같이 처리했습니다.
서버로부터의 Json 응답
{
"status": true,
"code": 200,
"message": "Success",
"data": {
"fullname": "Rohan",
"role": 1
}
}
공통 데이터 핸들러 클래스
public class ApiResponse<T> {
@SerializedName("status")
public boolean status;
@SerializedName("code")
public int code;
@SerializedName("message")
public String reason;
@SerializedName("data")
public T content;
}
커스텀 시리얼라이저
static class MyDeserializer<T> implements JsonDeserializer<T>
{
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
JsonElement content = je.getAsJsonObject();
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, type);
}
}
Gson 객체
Gson gson = new GsonBuilder()
.registerTypeAdapter(ApiResponse.class, new MyDeserializer<ApiResponse>())
.create();
API 호출
@FormUrlEncoded
@POST("/loginUser")
Observable<ApiResponse<Profile>> signIn(@Field("email") String username, @Field("password") String password);
restService.signIn(username, password)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<ApiResponse<Profile>>() {
@Override
public void onCompleted() {
Log.i("login", "On complete");
}
@Override
public void onError(Throwable e) {
Log.i("login", e.toString());
}
@Override
public void onNext(ApiResponse<Profile> response) {
Profile profile= response.content;
Log.i("login", profile.getFullname());
}
});
잊지 마세요.@SerializedName
그리고.@Expose
GSON에 의해 JSON에서 가장 역직렬화된 모든 클래스 멤버 및 내부 클래스 멤버에 대한 주석.
https://stackoverflow.com/a/40239512/1676736 를 참조해 주세요.
또 하나의 심플한 솔루션:
JsonObject parsed = (JsonObject) new JsonParser().parse(jsonString);
Content content = gson.fromJson(parsed.get("content"), Content.class);
더 간단한 방법이 있습니다. 그냥 생각해 보세요.content
서브 오브젝트를 다른 클래스로 지정합니다.
class Content {
var foo = 0
var bar: String? = null
}
class Response {
var statis: String? = null
var reason: String? = null
var content: Content? = null
}
그리고 이제 당신은Response
를 입력하여 json을 역직렬화합니다.
언급URL : https://stackoverflow.com/questions/23070298/get-nested-json-object-with-gson-using-retrofit
'programing' 카테고리의 다른 글
포맷하지 않고 자바독에서 "<"와 ">"를 사용하려면 어떻게 해야 합니까? (0) | 2022.09.03 |
---|---|
짧은 값 Java 설정 (0) | 2022.09.03 |
클래스의 개개의 에넘을 자바독하는 방법 (0) | 2022.09.03 |
IntelliJ가 '/path/to/tomcat/bin/catalina 프로그램을 실행할 수 없습니다.sh' error=13개의 권한이 거부되었습니다. (0) | 2022.09.03 |
Vuejs에서 "webpack Dev Server invalid Options"를 수정하는 방법 (0) | 2022.09.03 |