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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27550376/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 04:35:24  来源:igfitidea点击:

Spring @RestController custom JSON deserializer

javaspringrestHymansonspring-boot

提问by jakub.petr

I want to use custom JSON deserializer for some classes(Rolehere) but I can't get it working. The custom deserializer just isn't called.

我想对某些类使用自定义 JSON 反序列化器(此处为角色),但无法正常工作。只是没有调用自定义解串器。

I use Spring Boot 1.2.

我使用 Spring Boot 1.2。

Deserializer:

解串器:

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
    }
}

Controller:

控制器:

@RestController
public class RoleController {

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

    @JsonDeserialize(using = ModelDeserializer.class)
    public class Role extends Model {
    
    }
    
  2. Hymanson2ObjectMapperBuilderbean in Java Config

    @Bean
    public Hymanson2ObjectMapperBuilder HymansonBuilder() {
        Hymanson2ObjectMapperBuilder builder = new Hymanson2ObjectMapperBuilder();
        builder.deserializerByType(Role.class, new ModelDeserializer());
        return builder;
    }
    
  1. @JsonDeserialize关于角色

    @JsonDeserialize(using = ModelDeserializer.class)
    public class Role extends Model {
    
    }
    
  2. Hymanson2ObjectMapperBuilderJava 配置中的 bean

    @Bean
    public Hymanson2ObjectMapperBuilder HymansonBuilder() {
        Hymanson2ObjectMapperBuilder builder = new Hymanson2ObjectMapperBuilder();
        builder.deserializerByType(Role.class, new ModelDeserializer());
        return builder;
    }
    

What am I doing wrong?

我究竟做错了什么?

EDITIt is probably caused by @RestControllerbecause it works with @Controller...

编辑这可能是@RestController因为它适用于@Controller...

采纳答案by Ilya Ovesnov

First of all you don't need to override Hymanson2ObjectMapperBuilderto add custom deserializer. This approach should be used when you can't add @JsonDeserializeannotation. You should use @JsonDeserializeor override Hymanson2ObjectMapperBuilder.

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

What is missed is the @RequestBodyannotation:

遗漏的是@RequestBody注释:

@RestController
public class HymansonCustomDesRestEndpoint {

    @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;
    }
}

回答by povisenko

There is also another pretty interesting solution which can be helpful in case when you want to modify your JSON body before calling default deserializer. And let's imagine that you need to use some additional bean for that (use @Autowiremechanism)

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

Let's image situation, that you have the following controller:

让我们想象一下,您有以下控制器:

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

Where OrderProductInterfaceis:

在哪里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{}

The code above will provide dynamic deserialization base on filed providerTypeand validation according to concrete implementation. For better grasp, consider that OrderProductForARequestDatacan be something like that:

上面的代码将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;

}

And let's image now that we want to init somehow providerType(enrich input) before default deserialization will be executed.so the object will be deserialized properly according to the rule in OrderProductInterface. To do that you can just modify your @Configurationclass in the following way:

现在让我们想象一下,我们想在执行默认反序列化之前以某种方式初始化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);

           }

      }
}