一尘不染

Gson可选和必填字段

json

Gson与可选字段相比,应如何处理?

由于所有字段都是可选的,因此我无法根据响应json是否包含某些密钥来真正使我的网络请求失败,Gson只需将其解析为null。

我正在使用的方法 gson.fromJson(json, mClassOfT);

例如,如果我有以下json:

{"user_id":128591, "user_name":"TestUser"}

而我的班级:

public class User {

    @SerializedName("user_id")
    private String mId;

    @SerializedName("user_name")
    private String mName;

    public String getId() {
        return mId;
    }

    public void setId(String id) {
        mId = id;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }
}

Gson如果json不包含user_id或不包含user_name密钥,是否有任何选择会失败?

在许多情况下,您可能至少需要解析一些值,而其他值是可选的?

是否有任何模式或库可用于全局处理这种情况?

谢谢。


阅读 485

收藏
2020-07-27

共1个答案

一尘不染

如您所述,Gson无法定义“必填字段”,并且null如果JSON中缺少某些内容,您只会进入反序列化的对象。

这是一个可重复使用的反序列化器和注释,它将完成此任务。局限性在于,如果POJO仍然需要自定义反序列化器,则您必须走得更远,或者Gson在构造函数中传入一个对象以反序列化为对象本身,或者将注解签出到单独的方法中并使用它在您的解串器中。您还可以通过创建自己的异常并将其传递给来改进异常处理,JsonParseException以便可以getCause()在调用方中对其进行检测。

综上所述,在绝大多数情况下,这将起作用:

public class App
{

    public static void main(String[] args)
    {
        Gson gson =
            new GsonBuilder()
            .registerTypeAdapter(TestAnnotationBean.class, new AnnotatedDeserializer<TestAnnotationBean>())
            .create();

        String json = "{\"foo\":\"This is foo\",\"bar\":\"this is bar\"}";
        TestAnnotationBean tab = gson.fromJson(json, TestAnnotationBean.class);
        System.out.println(tab.foo);
        System.out.println(tab.bar);

        json = "{\"foo\":\"This is foo\"}";
        tab = gson.fromJson(json, TestAnnotationBean.class);
        System.out.println(tab.foo);
        System.out.println(tab.bar);

        json = "{\"bar\":\"This is bar\"}";
        tab = gson.fromJson(json, TestAnnotationBean.class);
        System.out.println(tab.foo);
        System.out.println(tab.bar);
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface JsonRequired
{
}

class TestAnnotationBean
{
    @JsonRequired public String foo;
    public String bar;
}

class AnnotatedDeserializer<T> implements JsonDeserializer<T>
{

    public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
    {
        T pojo = new Gson().fromJson(je, type);

        Field[] fields = pojo.getClass().getDeclaredFields();
        for (Field f : fields)
        {
            if (f.getAnnotation(JsonRequired.class) != null)
            {
                try
                {
                    f.setAccessible(true);
                    if (f.get(pojo) == null)
                    {
                        throw new JsonParseException("Missing field in JSON: " + f.getName());
                    }
                }
                catch (IllegalArgumentException ex)
                {
                    Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);
                }
                catch (IllegalAccessException ex)
                {
                    Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return pojo;

    }
}

输出:

这是foo
这是酒吧
这是foo
空值
线程“ main” com.google.gson.JsonParseException中的异常:JSON中的字段缺失:foo
2020-07-27