有没有办法忽略 Gson 中的 JsonSyntaxException
Is there a way to ignore JsonSyntaxException in Gson
我有一个 json 看起来像这样:
[
{
_id: "54b8f62fa08c286b08449b8f",
loc: [
36.860983,
31.0567
]
},
{
_id: "54b8f6aea08c286b08449b93",
loc: {
coordinates: [ ]
}
}
]
如您所见,loc 对象有时是一个 json 对象,有时是一个双精度数组。在不编写自定义反序列化程序的情况下,有没有办法避免 JsonSyntaxException
并将 loc 对象设置为 null 当它是 json 对象而不是双精度数组时。
没有任何简单的方法(我的意思是 property/method 在 Gson 调用)自定义 seralization/deserialization 特定字段 在 json 值。
您可以查看com.google.gson.internal.bind.ReflectiveTypeAdapterFactory的源代码,并调试其内部class Adapter
的read
方法。 (那是你的 JsonSyntaxException
发生的地方)
您可以阅读 Custom serialization for JUST specific fields 并跟踪其链接。它可能会在 Gson 的未来版本中实现。 (在最新版本 2.2.4 中不可用)
我会为此编写一些代码。也许这不是您要找的东西,但它可能会对其他人有所帮助。)
解决方案1(与第二种解决方案相比代码较少,但第二种解决方案的性能要好得多):
public class SubClass extends BaseClass {
private double[] loc;
}
public class BaseClass {
@SerializedName("_id")
private String id;
}
public class CustomTypeAdapter extends TypeAdapter<BaseClass> {
private Gson gson;
public CustomTypeAdapter() {
this.gson = new Gson();
}
@Override
public void write(JsonWriter out, BaseClass value)
throws IOException {
throw new RuntimeException("Not implemented for this question!");
}
@Override
public BaseClass read(JsonReader in) throws IOException {
BaseClass instance;
try {
instance = gson.fromJson(in, SubClass.class);
} catch (Exception e) {
e.printStackTrace();
instance = gson.fromJson(in, BaseClass.class);
}
return instance;
}
}
测试:
private void test() {
String json = "[{_id:\"54b8f62fa08c286b08449b8f\",loc:[36.860983,31.0567]},{_id:\"54b8f6aea08c286b08449b93\",loc:{coordinates:[]}}]";
Type collectionType = new TypeToken<List<BaseClass>>(){}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(BaseClass.class, new CustomTypeAdapter()).create();
List<BaseClass> list = gson.fromJson(json, collectionType);
for(BaseClass item : list) {
if(item instanceof SubClass) {
System.out.println("item has loc value");
SubClass subClassInstance = (SubClass)item;
} else {
System.out.println("item has no loc value");
BaseClass baseClassInstance = item;
}
}
}
解决方案 2(这是 Gson 开发人员的建议之一。See original post.):
将下面 class 复制到您的项目中。它将成为您自定义 TypeAdapterFactory 的基础 class。
public abstract class CustomizedTypeAdapterFactory<C>
implements TypeAdapterFactory {
private final Class<C> customizedClass;
public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
this.customizedClass = customizedClass;
}
@SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return type.getRawType() == customizedClass
? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
: null;
}
private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<C>() {
@Override public void write(JsonWriter out, C value) throws IOException {
JsonElement tree = delegate.toJsonTree(value);
beforeWrite(value, tree);
elementAdapter.write(out, tree);
}
@Override public C read(JsonReader in) throws IOException {
JsonElement tree = elementAdapter.read(in);
afterRead(tree);
return delegate.fromJsonTree(tree);
}
};
}
/**
* Override this to muck with {@code toSerialize} before it is written to
* the outgoing JSON stream.
*/
protected void beforeWrite(C source, JsonElement toSerialize) {
}
/**
* Override this to muck with {@code deserialized} before it parsed into
* the application type.
*/
protected void afterRead(JsonElement deserialized) {
}
}
编写您的 POJO 和您的自定义 CustomizedTypeAdapterFactory。覆盖 afterRead
方法并按照您在问题中的要求处理双数组:
public class MyClass {
@SerializedName("_id")
private String id;
private double[] loc;
// getters/setters
}
private class MyClassTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyClass> {
private MyClassTypeAdapterFactory() {
super(MyClass.class);
}
@Override protected void afterRead(JsonElement deserialized) {
try {
JsonArray jsonArray = deserialized.getAsJsonObject().get("loc").getAsJsonArray();
System.out.println("loc is not a double array, its ignored!");
} catch (Exception e) {
deserialized.getAsJsonObject().remove("loc");
}
}
}
测试:
private void test() {
String json = "[{_id:\"54b8f62fa08c286b08449b8f\",loc:[36.860983,31.0567]},{_id:\"54b8f6aea08c286b08449b93\",loc:{coordinates:[]}}]";
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
.create();
Type collectionType = new TypeToken<List<MyClass>>(){}.getType();
List<MyClass> list = gson.fromJson(json, collectionType);
for(MyClass item : list) {
if(item.getLoc() != null) {
System.out.println("item has loc value");
} else {
System.out.println("item has no loc value");
}
}
}
我就是这样做的。它更短,但我认为 @DevrimTuncer 的回答是最好的。
//This is just Double array to use as location object
public class Location extends ArrayList<Double> {
public Double getLatidute() {
if (this.size() > 0) {
return this.get(0);
} else {
return (double) 0;
}
}
public Double getLongitude() {
if (this.size() > 1) {
return this.get(1);
} else {
return (double) 0;
}
}
public static class LocationDeserializer implements JsonDeserializer<Location> {
@Override
public Location deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
JsonArray array = json.getAsJsonArray();
Location location = new Location();
for (int i = 0; i < array.size(); i++) {
location.add(array.get(i).getAsDouble());
}
return location;
} catch (Exception e) {
return null;
}
}
}
}
我有一个 json 看起来像这样:
[
{
_id: "54b8f62fa08c286b08449b8f",
loc: [
36.860983,
31.0567
]
},
{
_id: "54b8f6aea08c286b08449b93",
loc: {
coordinates: [ ]
}
}
]
如您所见,loc 对象有时是一个 json 对象,有时是一个双精度数组。在不编写自定义反序列化程序的情况下,有没有办法避免 JsonSyntaxException
并将 loc 对象设置为 null 当它是 json 对象而不是双精度数组时。
没有任何简单的方法(我的意思是 property/method 在 Gson 调用)自定义 seralization/deserialization 特定字段 在 json 值。
您可以查看com.google.gson.internal.bind.ReflectiveTypeAdapterFactory的源代码,并调试其内部class Adapter
的read
方法。 (那是你的 JsonSyntaxException
发生的地方)
您可以阅读 Custom serialization for JUST specific fields 并跟踪其链接。它可能会在 Gson 的未来版本中实现。 (在最新版本 2.2.4 中不可用)
我会为此编写一些代码。也许这不是您要找的东西,但它可能会对其他人有所帮助。)
解决方案1(与第二种解决方案相比代码较少,但第二种解决方案的性能要好得多):
public class SubClass extends BaseClass {
private double[] loc;
}
public class BaseClass {
@SerializedName("_id")
private String id;
}
public class CustomTypeAdapter extends TypeAdapter<BaseClass> {
private Gson gson;
public CustomTypeAdapter() {
this.gson = new Gson();
}
@Override
public void write(JsonWriter out, BaseClass value)
throws IOException {
throw new RuntimeException("Not implemented for this question!");
}
@Override
public BaseClass read(JsonReader in) throws IOException {
BaseClass instance;
try {
instance = gson.fromJson(in, SubClass.class);
} catch (Exception e) {
e.printStackTrace();
instance = gson.fromJson(in, BaseClass.class);
}
return instance;
}
}
测试:
private void test() {
String json = "[{_id:\"54b8f62fa08c286b08449b8f\",loc:[36.860983,31.0567]},{_id:\"54b8f6aea08c286b08449b93\",loc:{coordinates:[]}}]";
Type collectionType = new TypeToken<List<BaseClass>>(){}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(BaseClass.class, new CustomTypeAdapter()).create();
List<BaseClass> list = gson.fromJson(json, collectionType);
for(BaseClass item : list) {
if(item instanceof SubClass) {
System.out.println("item has loc value");
SubClass subClassInstance = (SubClass)item;
} else {
System.out.println("item has no loc value");
BaseClass baseClassInstance = item;
}
}
}
解决方案 2(这是 Gson 开发人员的建议之一。See original post.):
将下面 class 复制到您的项目中。它将成为您自定义 TypeAdapterFactory 的基础 class。
public abstract class CustomizedTypeAdapterFactory<C>
implements TypeAdapterFactory {
private final Class<C> customizedClass;
public CustomizedTypeAdapterFactory(Class<C> customizedClass) {
this.customizedClass = customizedClass;
}
@SuppressWarnings("unchecked") // we use a runtime check to guarantee that 'C' and 'T' are equal
public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return type.getRawType() == customizedClass
? (TypeAdapter<T>) customizeMyClassAdapter(gson, (TypeToken<C>) type)
: null;
}
private TypeAdapter<C> customizeMyClassAdapter(Gson gson, TypeToken<C> type) {
final TypeAdapter<C> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<C>() {
@Override public void write(JsonWriter out, C value) throws IOException {
JsonElement tree = delegate.toJsonTree(value);
beforeWrite(value, tree);
elementAdapter.write(out, tree);
}
@Override public C read(JsonReader in) throws IOException {
JsonElement tree = elementAdapter.read(in);
afterRead(tree);
return delegate.fromJsonTree(tree);
}
};
}
/**
* Override this to muck with {@code toSerialize} before it is written to
* the outgoing JSON stream.
*/
protected void beforeWrite(C source, JsonElement toSerialize) {
}
/**
* Override this to muck with {@code deserialized} before it parsed into
* the application type.
*/
protected void afterRead(JsonElement deserialized) {
}
}
编写您的 POJO 和您的自定义 CustomizedTypeAdapterFactory。覆盖 afterRead
方法并按照您在问题中的要求处理双数组:
public class MyClass {
@SerializedName("_id")
private String id;
private double[] loc;
// getters/setters
}
private class MyClassTypeAdapterFactory extends CustomizedTypeAdapterFactory<MyClass> {
private MyClassTypeAdapterFactory() {
super(MyClass.class);
}
@Override protected void afterRead(JsonElement deserialized) {
try {
JsonArray jsonArray = deserialized.getAsJsonObject().get("loc").getAsJsonArray();
System.out.println("loc is not a double array, its ignored!");
} catch (Exception e) {
deserialized.getAsJsonObject().remove("loc");
}
}
}
测试:
private void test() {
String json = "[{_id:\"54b8f62fa08c286b08449b8f\",loc:[36.860983,31.0567]},{_id:\"54b8f6aea08c286b08449b93\",loc:{coordinates:[]}}]";
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new MyClassTypeAdapterFactory())
.create();
Type collectionType = new TypeToken<List<MyClass>>(){}.getType();
List<MyClass> list = gson.fromJson(json, collectionType);
for(MyClass item : list) {
if(item.getLoc() != null) {
System.out.println("item has loc value");
} else {
System.out.println("item has no loc value");
}
}
}
我就是这样做的。它更短,但我认为 @DevrimTuncer 的回答是最好的。
//This is just Double array to use as location object
public class Location extends ArrayList<Double> {
public Double getLatidute() {
if (this.size() > 0) {
return this.get(0);
} else {
return (double) 0;
}
}
public Double getLongitude() {
if (this.size() > 1) {
return this.get(1);
} else {
return (double) 0;
}
}
public static class LocationDeserializer implements JsonDeserializer<Location> {
@Override
public Location deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
JsonArray array = json.getAsJsonArray();
Location location = new Location();
for (int i = 0; i < array.size(); i++) {
location.add(array.get(i).getAsDouble());
}
return location;
} catch (Exception e) {
return null;
}
}
}
}