简体   繁体   English

如何将@RestController中的请求体转换为抽象值列表?

[英]How do I convert request body in a @RestController to a List of abstract values?

Let's say we have the following classes: 假设我们有以下类:

public abstract class Investment {

   private String investmentType;

   // getters & setters
}

public class Equity extends Investment {
}

public class Bond extends Investment {
}

public class InvestmentFactory {

    public static Investment getTypeFromString(String investmentType) {
        Investment investment = null;
        if ("Bond".equals(investmentType)) {
            investment = new Bond();
        } else if ("Equity".equals(investmentType)) {
            investment = new Equity();
        } else {
            // throw exception
        }
        return investment;
    }
}

And the following @RestController : 以下@RestController

@RestController
public class InvestmentsRestController {

    private InvestmentRepository investmentRepository;

    @Autowired
    public InvestmentsRestController(InvestmentRepository investmentRepository) {
        this.investmentRepository = investmentRepository;
    }

    @RequestMapping(RequestMethod.POST)
    public List<Investment> update(@RequestBody List<Investment> investments) {
       return investmentRepository.update(investments);
    }

}

And the following json in the request body: 以及请求体中的以下json:

[
  {"investmentType":"Bond"},
  {"investmentType":"Equity"}
]

How can I bind or convert the json to a request body of List<Investment> without using Jackson's @JsonSubTypes on abstract class Investment , and instead use the InvestmentFactory ? 如何在不使用Jackson的抽象类Investment@JsonSubTypes情况下将json绑定或转换为List<Investment>的请求主体,而是使用InvestmentFactory

@JsonDeserialize works great, but if you have more fields than just the type then you will have to set them all manually. @JsonDeserialize效果很好,但是如果你有更多的字段而不仅仅是类型,那么你必须手动设置它们。 If you were to go back to Jackson, you can use: 如果你要回到杰克逊,你可以使用:

Investment.class Investment.class

    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.PROPERTY,
            property = "investmentType")
    @JsonTypeIdResolver(InvestmentResolver.class)
    public abstract class Investment {
    } 

InvestmentResolver.class InvestmentResolver.class

public class InvestmentResolver extends TypeIdResolverBase {

    @Override
    public JavaType typeFromId(DatabindContext context, String id) throws IOException {
        Investment investment = InvestmentFactory.getTypeFromString(type);
        return context.constructType(investment.getClass());
    }

The beauty of this is that if you start adding fields to Investment you won't have to add them in the Desrializer (at least, this happened to me in my case), but instead Jackson will take care of it for you. 这样做的好处是,如果你开始向投资添加字段,你就不必在Desrializer中添加它们(至少,在我的情况下这发生在我身上),但杰克逊会为你照顾它。 So tomorrow you can have the test case: 所以明天你可以得到测试用例:

'[{"investmentType":"Bond","investmentName":"ABC"},{"investmentType":"Equity","investmentName":"APPL"}]'

You should be good to go! 你应该好好去!

If you can use @JsonDeserialize : 如果你可以使用@JsonDeserialize

@JsonDeserialize(using = InvestmentDeserializer.class)
public abstract class Investment {
}



public class InvestmentDeserializer extends JsonDeserializer<Investment> {
    @Override
    public Investment deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        ObjectMapper objectMapper = (ObjectMapper) p.getCodec();
        TreeNode node = objectMapper.readTree(p);

        TreeNode investmentNode = node.get("investmentType");
        String type = objectMapper.readValue(investmentNode.traverse(objectMapper), String.class);
        return InvestmentFactory.getTypeFromString(type);
    }
}

Example controller: 示例控制器:

@RestController
public class MyController {
    @RequestMapping("/")
    public List<Class<?>> update(@RequestBody List<Investment> investments) {
        return investments.stream().map(Object::getClass).collect(Collectors.toList());
    }
}

Testing: 测试:

$ curl localhost:8080 -H "Content-Type: application/json" -d '[{"investmentType":"Bond"},{"investmentType":"Equity"}]'

Output: 输出:

["com.example.demo.Bond","com.example.demo.Equity"]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM