有没有办法忽略Gson中的JsonSyntaxException

时间:2015-02-16 15:30:41

标签: json gson

我有一个看起来像这样的json:

[
{
_id: "54b8f62fa08c286b08449b8f",
loc: [
   36.860983,
   31.0567
]
},
{
_id: "54b8f6aea08c286b08449b93",
loc: {
coordinates: [ ]
}
}
]

如您所见,loc对象有时是一个json对象,有时候是一个双数组。在没有编写自定义反序列化器的情况下,有没有办法避免JsonSyntaxException并将loc对象设置为null,当它是json对象而不是double数组时。

2 个答案:

答案 0 :(得分:1)

对于在json值处的特定字段的自定义分类/反序列化,没有任何简单的方法(我的意思是Gson的属性/方法调用)。

您可以查看com.google.gson.internal.bind.ReflectiveTypeAdapterFactory的源代码,并对其内部类Adapter的{​​{1}}方法进行调试。 (那是read发生的地方)

您可以阅读Custom serialization for JUST specific fields并跟踪其链接。它可以在Gson的未来版本中实现。 (最新版本2.2.4不提供)

我会为此编写一些代码。也许这不是你想要的,但它可能会帮助其他人。)

解决方案1 ​​(与第二种解决方案相比,代码更少,但第二种解决方案的性能要好得多):

JsonSyntaxException

<强>测试

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;
    }
}

解决方案2 (这是Gson Developers建议之一。See original post.):

将下面的课程复制到您的项目中。它将成为您的自定义TypeAdapterFactory的基类。

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;
        }
    }
}

编写您的POJO和您的自定义CustomizedTypeAdapterFactory。根据您提出的问题覆盖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) { } } 方法并处理双数组:

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");
        }
    }
}

答案 1 :(得分:0)

这就是我这样做的方式。它更短,但我认为 @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;
            }
        }
    }
}