当前位置: 首页 > 面试题库 >

让Gson在错误的类型上抛出异常

庾兴发
2023-03-14
问题内容

我在项目中使用Gson将JSON字符串反序列化为Java对象。如果我提出请求,我希望服务器发出明确定义的响应。服务器将返回我期望的定义明确的响应,或者将返回一个(也定义为)错误对象。

讲清楚一点:假设我有一个简单的对象,像这样:

class Dummy{
   private String foo;
   private int bar;
}

和这样的错误对象:

class ErrorHolder{
   private RequestError error;
}

class RequestError{
    private String publicMsg;
    private String msg;
}

如果我收到类似的服务器响应

{"foo":"Hello World", "bar":3 }

一切都按预期进行。

但是如果回应是这样的

{"error":{"publicMsg":"Something bad happened", msg:"you forgot requesting some parameter"}}

我会得到一个Dummy对象,那里foonullbar是0!Gson文档(来自Json)明确指出:

抛出JsonSyntaxException-如果json不是classOfT类型的对象的有效表示形式

因此,如果我尝试像这样解析第二个响应,我希望得到一个JsonSyntaxException:

Dummy dummy = Gson.fromJson(secondResponse, Dummy.class);

因为Json并不代表虚拟对象,而是代表ErrorHolder对象。

所以我的问题是:有没有办法让Gson以某种方式检测到错误的类型并引发异常?


问题答案:

不幸的是,那里的文档有些误导。

仅当您的类的字段类型与JSON中的字段不匹配时,它才会引发异常,即使这样做,它也会做出一些疯狂的尝试来对其进行修复(例如int,将JSON中的转换为String类中的)
)。如果您Date在POJO中有类似字段的内容,并且int在JSON中遇到了,则会将其抛出。静默忽略JSON中存在但POJO中不存在的字段,JSON中缺失但POJO中存在的字段设置为null

当前,GSON不提供用于任何形式的“严格”反序列化的机制,在该机制中,@RequiredPOJO中的字段将具有诸如注释的含义。

在您的情况下…我只是将POJO扩展为包括一个内部错误对象…类似于:

class Dummy {
   private String foo;
   private int bar;
   private Error error;

   private class Error {
        String publicMsg;
        String msg;
   }

   public boolean isError() {
       return error != null;
   }

   // setters and getters for your data, the error msg, etc.
}

您的另一个选择是编写一个自定义反序列化器,如果JSON是错误,则抛出该异常,例如:

class MyDeserializer implements JsonDeserializer<Dummy>
{
    @Override
    public Dummy deserialize(JsonElement json, Type typeOfT, 
                              JsonDeserializationContext context)
                    throws JsonParseException
    {
        JsonObject jsonObject = (JsonObject) json;

        if (jsonObject.get("error") != null)
        {
            throw new JsonParseException("Error!");
        }

        return new Gson().fromJson(json, Dummy.class);
    }
}

编辑添加: 有人最近对此进行了投票,并重新阅读了它,我认为“嗯,你知道的,你可以自己做,这也许很方便”。

这是一个可重复使用的反序列化器和注释,它将完全执行OP想要的操作。局限性在于,如果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


 类似资料:
  • 假设我有这样一个函数: 我想测试它是否使用Jest抛出一个(不仅仅是一个)。 然而,这项测试通过了。 如何检查Jest中异步抛出的错误类型?

  • 我是jUnit和mockito的新手。对于虚空的嘲弄是如何发挥作用的,我完全感到困惑。在这里,如果名称是“hello”,则函数抛出一个异常。但当我测试它时,它并没有抛出异常... 测试 }

  • //为(int i=0; i创建具有100个存储空间的数组 //提示用户输入要查找的索引

  • 问题内容: 我试图在Netbeans中重构一个大型程序,但我有点迷茫。我从来没有非常模块化,但是现在通过实际学习如何做到这一点来尝试纠正这种情况,并在将来纠正这种情况。不幸的是,我在将某些教程翻译成我的程序时遇到了麻烦。所以我希望这里有人可以帮忙。目前,我正在尝试分解一部分采用特定格式的文件并制成表格的代码。我知道我需要创建一个类并使用它来创建表对象,但是我不确定如何做。我有一个主文件,用于获取文

  • 问题内容: 这是一些示例代码: 如果我运行该程序并将其设置为 like ,则一切正常。 另一方面,如果我回答,那不会嘲笑我的有趣笑话。相反,我得到这个(如预期的那样): 有没有一种方法可以使它忽略不是int的条目,或者用“有多少个入侵者”重新提示?我想知道这两种方法。 问题答案: 你可以使用的诸多一个是方法有预验证。 这可以防止从甚至被抛出,因为你总是确保它WILL你读它之前匹配。 java.ut

  • 问题内容: 我正在使用Retrofit(与OkHttp和GSON结合使用)与在线Web服务进行通信。该Web服务的所有响应都有一个默认包装,类似于: 在此示例中,将为或。此外,仅在处理请求时发生错误时才包含任何内容。最后但并非最不重要的一点将包含调用的实际结果(示例中为字符串,但是某些调用返回JSON数组或JSON对象)。 为了处理此元数据,我创建了一个通用类,如下所示: 我还创建了代表有时给出的