SRF
SRF

Reputation: 597

How to deserialize json to enum with jackson in java?

I have an interface with name Field as below:

public interface Field {
}

This interface is in the module A. I have an enum called BField which is in module B and is implemented as below:

public enum BField implements Field {
    SOME_FIELD
}

There is a class named C in the module A as below:

public class C implements Serializable {
    private Set<Field> f;

    public Set<Field> getF() { return f; }
    public void setF(Set<Field> f) { this f = f; }
}

I have a REST method as below:

@RequestMapping(method=RequestMethod.Post, value="/save")
@ResponseBody
public void save (@RequestBody C c) {
    //save c
}

I send this JSON object to this method:

{
    "f": ["SOME_FIELD"]
}

then I get HTTP 400 bad request error code with the following exception log:

abstract types can only be instantiated with additional type information

The hierarchy of the modules is module B is dependent to module A. I tried to use @JsonTypeInfo but the dependency between modules works as a limit and does not let me to use BField.class in the @JsonSubTypes annotation for the field f in class C.

Upvotes: 1

Views: 1459

Answers (2)

SRF
SRF

Reputation: 597

At last I find the solution.

  1. I remove <mvc:annotation-driven/> in my context
  2. Add @JsonDeserialize(as = EnumSet.class) annotation for field private Set<Field> f; in class C.
  3. create a class called JsonBFieldDeserializer in module B as below:

    public class JsonBFieldDeserializer extends StdDeserializer<Field> {
        public JsonBFieldDeserializer() {
            this(null);
        }
        public JsonBFieldDeserializer(Class<?> vc) {
            super(vc);
        }
        @Overrid
        public Field deserialize(JsonParser jsonParser, DeserializationContext dC) throws IOException, JsonProcessingException {
            JsonNode node = jsonParser.getCodec().readTree();
            String text = node.asText();
            return BField.valueOf(text);
        }
    }
    
  4. Create a class called BConfiguration in module B as below:

    @Configuration
    public class BConfiguration extends WebMVCConfigurationSupport {
        protected void configureMessageConverters(List<HttpMessageConverters<?>> converters){
            converters.add(convert());
            addDefaultHttpMessageConverters(converters);
        }
        @Bean
        MappingJackson2HttpMessageConverter convert(){
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            ObjectMapper objectMapper = new ObjectMapper();
            SimpleModule module = new SimpleModule();
            module.addDeserializer(Field.class, new JsonBFieldDeserializer());
            objectMapper.registerModule(module);
            converter.setObjectMapper(objectMapper);
            return converter;
        }
    }
    
  5. Be careful to use fasterxml jackson not codehaus library!

Upvotes: 0

Costi Ciudatu
Costi Ciudatu

Reputation: 38255

The problem here is not the enum, it's the Set and the Field interfaces.

You need to tell Jackson what kind of Set and what kind of Field you want, and you can do that by annotating that property with:

@JsonDeserialize(as = EnumSet.class, contentAs = BField.class)

Upvotes: 1

Related Questions