有没有办法配置Gson,以便它将任何失败的字段解析视为null而不是抛出一个解析异常?理想情况下,我们可以捕获并记录异常 - 但是我们希望选项继续使用该程序,即使某些字段(或子字段)没有按预期进行解析。
示例:
格式错误的JSON:
{
"dog": []
}
使用课程:
class Farm {
public Dog dog;
}
class Dog {
public String name;
}
Gson gson = new Gson();
Farm oldMcdonald = gson.fromJson(json, Farm.class); // should not throw exception
assertNull(oldMcdonald.dog); // should pass
答案 0 :(得分:1)
在Gson中,它可以非常简单地实现。 尽管以下解决方案,我猜,似乎在任何情况下都不起作用(例如,原语),如果有必要可以增强它。
final class JsonFailSafeTypeAdapterFactory
implements TypeAdapterFactory {
private static final TypeAdapterFactory instance = new JsonFailSafeTypeAdapterFactory();
private JsonFailSafeTypeAdapterFactory() {
}
static TypeAdapterFactory get() {
return instance;
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// We can support non-primitive types only
if ( typeToken.getRawType().isPrimitive() ) {
return null;
}
final TypeAdapter<T> delegateTypeAdapter = gson.getAdapter(typeToken);
return new JsonFailSafeTypeAdapter<>(delegateTypeAdapter);
}
private static final class JsonFailSafeTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegateTypeAdapter;
private JsonFailSafeTypeAdapter(final TypeAdapter<T> delegateTypeAdapter) {
this.delegateTypeAdapter = delegateTypeAdapter;
}
@Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegateTypeAdapter.write(out, value);
}
@Override
public T read(final JsonReader in)
throws IOException {
try {
return delegateTypeAdapter.read(in);
} catch ( final MalformedJsonException | RuntimeException ignored ) {
// Once we get into unexpected JSON token, let's *always* consider a fallback to the default value
// Well, the default is always `null` anyway, but we'll do more work
return fallback(in);
}
}
private static <T> T fallback(final JsonReader in)
throws IOException {
final JsonToken jsonToken = in.peek();
switch ( jsonToken ) {
case BEGIN_ARRAY:
case BEGIN_OBJECT:
case NAME:
case STRING:
case NUMBER:
case BOOLEAN:
case NULL:
// Assume we're at the beginning of a complex JSON value or a JSON primitive
in.skipValue();
break;
case END_ARRAY:
// Not sure if we skipValue() can fast-forward this one
in.endArray();
break;
case END_OBJECT:
// The same
in.endObject();
break;
case END_DOCUMENT:
// do nothing
break;
default:
throw new AssertionError(jsonToken);
}
// Just return null (at least at the moment)
return null;
}
}
}
现在只需注册上面的类型工厂来处理所有类型(如果我没弄错的话,java.lang.Object除外)。
private static final Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(JsonFailSafeTypeAdapterFactory.get())
.create();
public static void main(final String... args)
throws IOException {
try ( final JsonReader jsonReader = Resources.getPackageResourceJsonReader(Q50002961.class, "farm.json") ) {
final Farm oldMcdonald = gson.fromJson(jsonReader, Farm.class);
if ( oldMcdonald.dog != null ) {
throw new AssertionError();
}
System.out.println(oldMcdonald);
}
}
示例输出:
q50002961.Farm@626b2d4a
如果不需要全局注册工厂,另一个选项还是指定目标字段。例如:
final class Farm {
@JsonAdapter(JsonFailSafeTypeAdapterFactory.class)
final Dog dog = null;
}
答案 1 :(得分:0)
我会为您的问题发布解决方案,但仍需要您更改您身边的代码。例如,如果您已将属性配置为对象并且接收到数组 - 则无法正确映射该属性。所以我建议将代码中的所有内容更改为List并编写一个自定义映射器,在收到对象时创建一个包含一个元素的列表。这样,您可以灵活地接收所收到的内容,但是当您有多个对象到阵列时,您还需要添加一些逻辑来处理问题。举个例子,如果你有两只狗,你会怎么做?什么是正确的行为?
所以我会这样做:
public class MainClass {
public static <T> void main(String[] args) throws IOException {
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ArrayAdapterFactory()).create();
// Here I do the opposite - add one dog but expect a collection
String json = "{ \"dog\": {name=\"Snoopy\"} }";
Farm oldMcdonald = gson.fromJson(json, Farm.class); // should not throw exception
System.out.println("Dog:"+oldMcdonald.dog.get(0).name); //Works properly
}
}
class Farm {
@Expose
public List<Dog> dog; //All such properties become a list. You handle the situation when there are more than one values
}
class Dog {
@Expose
public String name;
}
class ArrayAdapter<T> extends TypeAdapter<List<T>> {
private Class<T> adapterclass;
public ArrayAdapter(Class<T> adapterclass) {
this.adapterclass = adapterclass;
}
public List<T> read(JsonReader reader) throws IOException {
List<T> list = new ArrayList<T>();
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new ArrayAdapterFactory())
.create();
if (reader.peek() == JsonToken.BEGIN_OBJECT) {
T inning = gson.fromJson(reader, adapterclass);
list.add(inning);
// return null; here if you want to return null instead of list with one element
} else if (reader.peek() == JsonToken.BEGIN_ARRAY) {
reader.beginArray();
while (reader.hasNext()) {
T inning = gson.fromJson(reader, adapterclass);
list.add(inning);
}
reader.endArray();
}
return list;
}
public void write(JsonWriter writer, List<T> value) throws IOException {
}
}
class ArrayAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
TypeAdapter<T> typeAdapter = null;
try {
if (type.getRawType() == List.class)
typeAdapter = new ArrayAdapter(
(Class) ((ParameterizedType) type.getType())
.getActualTypeArguments()[0]);
} catch (Exception e) {
e.printStackTrace();
}
return typeAdapter;
}
}