CustomDeserializer没有默认的(无参)构造函数。

26

我正在使用RestTemplate消费一个REST Api。我从API得到的响应包含很多嵌套对象。以下是一个小片段作为示例:

"formularios": [
  {
    "form_data_id": "123006",
    "form_data": {
      "form_data_id": "123006",
      "form_id": "111",
      "efs": {
        "1": {},
        "2": "{\"t\":\"c\",\"st\":\"m\",\"v\":[{\"id\":\"3675\",\"l\":\"a) Just an example\",\"v\":\"1\"},{\"id\":\"3676\",\"l\":\"b) Another example.\",\"v\":\"2\"}]}"
      }
    }

我遇到的问题是,大部分时候,“1”实际上有内容,就像“2”一样,但是jackson将其解析为对象“efs”的字符串。但是有时,就像在代码片段中一样,API发送它为空,并且jackson将其视为对象,这会导致错误,该错误涉及START_OBJECT(无法记住确切的错误,但对于此问题不重要)。
因此,我决定制作一个自定义反序列化器,当jackson读取“1”时,它会忽略空对象并将其解析为null字符串。
以下是我的自定义反序列化器:
public class CustomDeserializer extends StdDeserializer<Efs> {

 public CustomDeserializer(Class<Efs> t) {
     super(t);
 }

 @Override
 public Efs deserialize(JsonParser jp, DeserializationContext dc)
         throws IOException, JsonProcessingException {

     String string1 = null;
     String string2 = null;
     JsonToken currentToken = null;

     while ((currentToken = jp.nextValue()) != null) {
         if (currentToken.equals(JsonToken.VALUE_STRING)) {
             if (jp.getCurrentName().equals("1")) {
                 string1 = jp.getValueAsString();
             } else {
                 string2 = jp.getValueAsString();
             }

         } else {
             if (jp.getCurrentName().equals("2")) {
                 string2 = jp.getValueAsString();
             }

         }
     }
     return new Efs(string1, string2);

  }
 }

当我从API接收响应时,我使用的方式如下:

    ObjectMapper mapper = new ObjectMapper();  
    SimpleModule mod = new SimpleModule("EfsModule");
    mod.addDeserializer(Efs.class, new CustomDeserializer(Efs.class));
    mapper.registerModule(mod);


    List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
    MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
    jsonMessageConverter.setObjectMapper(mapper);
    messageConverters.add(jsonMessageConverter);


    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setMessageConverters(messageConverters);

I'm getting the error:

 CustomDeserializer has no default (no arg) constructor

但是我不知道具体哪里做错了,也不知道该如何解决。感谢您的帮助,对于这个长问题表示歉意,我想尽可能提供更多的背景信息。

2个回答

35

还有一个陷阱,用户可能会掉进去(就像我一样)。如果您将反序列化程序声明为内部类(而不是静态嵌套类),则会出现以下问题:

@JsonDeserialize(using = DomainObjectDeserializer.class)
public class DomainObject {
    private String key;

    public class DomainObjectDeserializer extends StdDeserializer<DomainObject> {
        public DomainObjectDeserializer() {
            super(DomainObject.class);
        }

        @Override
        public DomainObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            // code
        }
    }
}

Jackson使用没有参数的Class#getDeclaredConstructor()(该方法接受vararg),这意味着:给我一个默认(无参数)构造函数。当Jackson尝试创建DomainObjectDeserializer时,上面的代码会抛出异常,因为javac生成了接受封闭类引用的构造函数。严格来说,DomainObjectDeserializer没有默认构造函数。
出于好奇,您可以执行DomainObjectDeserializer.class.getDeclaredConstructors()并确保该方法返回包含具有封闭类引用的构造函数定义的单个元素数组。 DomainObjectDeserializer应该声明为静态类。
这里有一个很好的答案,可以阅读更多细节。

27

你需要一个默认的无参构造函数。你可以创建一个(或者替换其他的,如果你不是真的需要它):

public class CustomDeserializer extends StdDeserializer<Efs> {

   public CustomDeserializer() {
       super(Efs.class);
   }
   ...
}

1
我如何在类列表(List)中操作?List<Efs>?尝试使用 this(null),但不起作用。 - Nephilim
1
@Nephilim 使用接受JavaType的父构造函数,并使用TypeFactory传入所需类型。 - Edgar Asatryan
这在由GraalVM生成的本地镜像中不起作用。 - zipper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接