答案 0 :(得分:3)
以下是我解决这个问题的方法:
在模型中创建自定义类型适配器并手动解析;
public class AccountState {
//@SerializedName("rated") //NOPE, parse it manually
private Integer mRated; //also don't name it rated
public Integer getRated() {
return mRated;
}
public void setRated(Integer rated) {
this.mRated = rated;
}
public static class AccountStateDeserializer implements JsonDeserializer<AccountState> {
@Override
public AccountState deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
AccountState accountState = new Gson().fromJson(json, AccountState.class);
JsonObject jsonObject = json.getAsJsonObject();
if (jsonObject.has("rated")) {
JsonElement elem = jsonObject.get("rated");
if (elem != null && !elem.isJsonNull()) {
if(elem.isJsonPrimitive()){
accountState.setRated(null);
}else{
accountState.setRated(elem.getAsJsonObject().get("value").getAsInt());
}
}
}
return accountState ;
}
}
}
在这里,您可以使用此自定义适配器创建您的gson:
final static Gson gson = new GsonBuilder()
.registerTypeAdapter(AccountState.class, new AccountState.AccountStateDeserializer())
.create();
将其添加到改装中:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.ENDPOINT)
.addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
.build();
TADADADADADADADDAD!
答案 1 :(得分:2)
您无需实现自定义转换器即可使其正常工作。 您所要做的就是为变量添加一个通用的“对象”类型,然后通过执行此操作来检查它是哪种数据类型:
if(object.getClass == YourClass.class){
Whatever we = ((YourClass) object).getWhatever();
} else if(object.getClass == YourOtherClass.class){
String name = ((YourOtherClass) object).getName();
}
您可以根据需要为此变量添加任意数量的数据类型。 您还可以使用java类型“String.class”,“Boolean.class”或任何您喜欢的类型。
答案 2 :(得分:0)
Gson有一个很好的功能,允许将自定义类型适配器或类型适配器工厂注入某个字段,因此让Gson管理主机对象和后者的字段(反)序列化。因此,您可以确保AccountState
仍然可以使用ReflectiveTypeAdapterFactory
和ReflectiveTypeAdapterFactory.Adapter
进行反序列化,因此可以应用GsonBuilder中定义的所有反序列化策略。
final class AccountState {
// This is what can make life easier. Note its advantages:
// * PackedBooleanTypeAdapterFactory can be reused multiple times
// * AccountState life-cycle can be managed by Gson itself,
// so it can manage *very* complex deserialization automatically.
@JsonAdapter(PackedBooleanTypeAdapterFactory.class)
final Boolean rated = null;
}
接下来,如何实施PackageBooleanTypeAdapterFactory
:
final class PackedBooleanTypeAdapterFactory
implements TypeAdapterFactory {
// Gson can instantiate this itself, no need to expose
private PackedBooleanTypeAdapterFactory() {
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Check if it's the type we can handle ourself
if ( typeToken.getRawType() == Boolean.class ) {
final TypeAdapter<Boolean> typeAdapter = new PackedIntegerTypeAdapter(gson);
// Some Java "unchecked" boilerplate here...
@SuppressWarnings("unchecked")
final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) typeAdapter;
return castTypeAdapter;
}
// If it's something else, let Gson pick a downstream type adapter on its own
return null;
}
private static final class PackedIntegerTypeAdapter
extends TypeAdapter<Boolean> {
private final Gson gson;
private PackedIntegerTypeAdapter(final Gson gson) {
this.gson = gson;
}
@Override
public void write(final JsonWriter out, final Boolean value) {
throw new UnsupportedOperationException();
}
@Override
public Boolean read(final JsonReader in)
throws MalformedJsonException {
// Pick next token as a JsonElement
final JsonElement jsonElement = gson.fromJson(in, JsonElement.class);
// Note that Gson uses JsonNull singleton to denote a null
if ( jsonElement.isJsonNull() ) {
return null;
}
if ( jsonElement.isJsonPrimitive() ) {
return jsonElement
.getAsJsonPrimitive()
.getAsBoolean();
}
if ( jsonElement.isJsonObject() ) {
return jsonElement
.getAsJsonObject()
.getAsJsonPrimitive("value")
.getAsBoolean();
}
// Not something we can handle
throw new MalformedJsonException("Cannot parse: " + jsonElement);
}
}
}
演示:
public static void main(final String... args) {
parseAndDump("{\"rated\":null}");
parseAndDump("{\"rated\":true}");
parseAndDump("{\"rated\":{\"value\":true}}");
}
private static void parseAndDump(final String json) {
final AccountState accountState = gson.fromJson(json, AccountState.class);
System.out.println(accountState.rated);
}
输出:
空
真正
真的
请注意,JsonSerializer
和JsonDeserializer
由于其树模型设计而具有一些性能和内存成本(只要它们在内存中,您就可以轻松遍历JSON树)。有时,对于简单的情况,流式类型适配器可能更可取。优点:消耗更少的内存并且工作更快。缺点:难以实施。
final class AccountState {
@JsonAdapter(PackedBooleanTypeAdapter.class)
final Boolean rated = null;
}
请注意,rated
字段直接接受类型适配器,因为它不需要Gson
个实例来构建JSON树(JsonElement
s)。
final class PackedBooleanTypeAdapter
extends TypeAdapter<Boolean> {
// Gson still can instantiate this type adapter itself
private PackedBooleanTypeAdapter() {
}
@Override
public void write(final JsonWriter out, final Boolean value) {
throw new UnsupportedOperationException();
}
@Override
public Boolean read(final JsonReader in)
throws IOException {
// Peeking the next JSON token and dispatching parsing according to the given token
final JsonToken token = in.peek();
switch ( token ) {
case NULL:
return parseAsNull(in);
case BOOLEAN:
return parseAsBoolean(in);
case BEGIN_OBJECT:
return parseAsObject(in);
// The below might be omitted, since some code styles prefer all switch/enum constants explicitly
case BEGIN_ARRAY:
case END_ARRAY:
case END_OBJECT:
case NAME:
case STRING:
case NUMBER:
case END_DOCUMENT:
throw new MalformedJsonException("Cannot parse: " + token);
// Not a known token, and must never happen -- something new in a newer Gson version?
default:
throw new AssertionError(token);
}
}
private Boolean parseAsNull(final JsonReader in)
throws IOException {
// null token still has to be consumed from the reader
in.nextNull();
return null;
}
private Boolean parseAsBoolean(final JsonReader in)
throws IOException {
// Consume a boolean value from the reader
return in.nextBoolean();
}
private Boolean parseAsObject(final JsonReader in)
throws IOException {
// Consume the begin object token `{`
in.beginObject();
// Get the next property name
final String property = in.nextName();
// Not a value? Then probably it's not what we're expecting for
if ( !property.equals("value") ) {
throw new MalformedJsonException("Unexpected property: " + property);
}
// Assuming the property "value" value must be a boolean
final boolean value = in.nextBoolean();
// Consume the object end token `}`
in.endObject();
return value;
}
}
这个应该更快。输出保持不变。请注意,Gson在两种情况下都不需要GsonBuilder
。至于我记得Retrofit 2是如何工作的,GsonConverterFactory
仍然是必需的(不确定,Gson不是Retrofit 2中的默认序列化器吗?)。