Spring @RestController 自定义 JSON 反序列化器

Posted

技术标签:

【中文标题】Spring @RestController 自定义 JSON 反序列化器【英文标题】:Spring @RestController custom JSON deserializer 【发布时间】:2015-02-17 11:08:45 【问题描述】:

我想为某些类(Role 这里)使用自定义 JSON 反序列化器,但我无法让它工作。只是没有调用自定义反序列化程序。

我使用 Spring Boot 1.2。

反序列化器:

public class ModelDeserializer extends JsonDeserializer<Role> 

    @Override
    public Role deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException 
        return null; // this is what should be called but it isn't
    

控制器:

@RestController
public class RoleController 

    @RequestMapping(value = "/role", method = RequestMethod.POST)
    public Object createRole(Role role) 
        // ... this is called
    

    @JsonDeserialize 角色

    @JsonDeserialize(using = ModelDeserializer.class)
    public class Role extends Model 
    
    
    

    Jackson2ObjectMapperBuilder Java 配置中的 bean

    @Bean
    public Jackson2ObjectMapperBuilder jacksonBuilder() 
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.deserializerByType(Role.class, new ModelDeserializer());
        return builder;
    
    

我做错了什么?

EDIT这可能是由@RestController引起的,因为它适用于@Controller...

【问题讨论】:

【参考方案1】:

首先,您无需重写 Jackson2ObjectMapperBuilder 即可添加自定义反序列化程序。当您无法添加@JsonDeserialize 注释时,应使用此方法。您应该使用@JsonDeserialize 或覆盖Jackson2ObjectMapperBuilder

缺少的是@RequestBody注解:

@RestController
public class JacksonCustomDesRestEndpoint 

    @RequestMapping(value = "/role", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public Object createRole(@RequestBody Role role) 
        return role;
    


@JsonDeserialize(using = RoleDeserializer.class)
public class Role 
    // ......


public class RoleDeserializer extends JsonDeserializer<Role> 
    @Override
    public Role deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException 
        // .................
        return something;
    

【讨论】:

我需要做同样的事情,唯一的区别是我有一个 get 方法,在我的 pojo 对象中我使用带有 @JsonDeserialize 的 Date 类型,但是当我运行该方法时,我得到一个 http 400 . (spring 4.1.7 y jackson 2.7.4)【参考方案2】:

还有另一个非常有趣的解决方案,如果您想在调用默认反序列化器之前修改 JSON 主体,它会很有帮助。让我们假设您需要为此使用一些额外的 bean(使用 @Autowire 机制)

让我们想象一下你有以下控制器的情况:

@RequestMapping(value = "/order/product", method = POST)
public <T extends OrderProductInterface> RestGenericResponse orderProduct(@RequestBody @Valid T data) 
    orderService.orderProduct(data);
    return generateResponse();

OrderProductInterface 在哪里:

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSerialize(include = NON_EMPTY)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, visible = true, property = "providerType")
@JsonSubTypes(
              @JsonSubTypes.Type(value = OrderProductForARequestData.class, name = "A")
          )
public interface OrderProductInterface

上面的代码将提供基于providerType的动态反序列化,并根据具体实现进行验证。为了更好地掌握,请考虑OrderProductForARequestData 可以是这样的:

public class OrderProductForARequestData implements OrderProductInterface 

    @NotBlank(message = "is mandatory field.")
    @Getter @Setter
    private String providerId;

    @NotBlank(message = "is mandatory field.")
    @Getter @Setter
    private String providerType;

    @NotBlank(message = "is mandatory field.")
    @Getter @Setter
    private String productToOrder;


现在让我们想象一下,我们想要以某种方式初始化providerType(丰富输入)在执行默认反序列化之前。因此对象将根据OrderProductInterface 中的规则正确反序列化。 为此,您可以通过以下方式修改您的 @Configuration 类:

//here can be any annotation which will enable MVC/Boot 
@Configuration
public class YourConfiguration

    @Autowired
    private ObjectMapper mapper;

    @Autowired
    private ProviderService providerService;

    @Override
    public void setup() 
        super.setup();
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new BeanDeserializerModifier() 
            @Override
            public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) 

                if (beanDesc.getBeanClass() == OrderProductInterface.class) 
                    return new OrderProductInterfaceDeserializer(providerService, beanDesc);
                
                return deserializer;
            
        );

        mapper.registerModule(module);
    

    public static class OrderProductInterfaceDeserializer extends AbstractDeserializer 

            private static final long serialVersionUID = 7923585097068641765L;

            private final ProviderService providerService;

            OrderProductInterfaceDeserializer(roviderService providerService, BeanDescription beanDescription) 
                super(beanDescription);
                this.providerService = providerService;
            

            @Override
            public Object deserializeWithType(JsonParser p, DeserializationContext context, TypeDeserializer typeDeserializer) throws IOException 
                ObjectCodec oc = p.getCodec();
                JsonNode node = oc.readTree(p);

                //Let's image that we have some identifier for provider type and we want to detect it
                JsonNode tmp = node.get("providerId");
                Assert.notNull(tmp, "'providerId' is mandatory field");
                String providerId = tmp.textValue();
                Assert.hasText(providerId, "'providerId' can't be empty");

                // Modify node
                ((ObjectNode) node).put("providerType",providerService.getProvider(providerId));

                JsonFactory jsonFactory = new JsonFactory();
                JsonParser newParser = jsonFactory.createParser(node.toString());
                newParser.nextToken();

                return super.deserializeWithType(newParser, context, typeDeserializer);

           

      

【讨论】:

以上是关于Spring @RestController 自定义 JSON 反序列化器的主要内容,如果未能解决你的问题,请参考以下文章

Spring @RestController 用于匿名和授权用户的单一方法

Spring 注解中@RestController与@Controller的区别

Spring 注解中@RestController与@Controller的区别

Spring 注解中@RestController与@Controller的区别

Spring中@Controller和@RestController之间的区别

Spring @RestController 生成没有命名空间的 XML