代码之家  ›  专栏  ›  技术社区  ›  dukethrash

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

  •  5
  • dukethrash  · 技术社区  · 6 年前

    假设我们有以下课程:

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

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

    以及请求正文中的以下json:

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

    List<Investment> 不用杰克逊的 @JsonSubTypes 论抽象类 Investment ,而使用 InvestmentFactory

    2 回复  |  直到 6 年前
        1
  •  4
  •   Joe A    6 年前

    @JsonDeserialize工作得很好,但是如果您有比类型更多的字段,那么您将不得不手动设置它们。如果你要回到杰克逊,你可以使用:

    投资类

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

    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());
        }
    

    这样做的好处是,如果你开始在投资中添加字段,你就不必在Desrializer中添加字段(至少,在我的情况下,这种情况发生在我身上),而是Jackson会为你处理它。所以明天你就可以得到测试用例了:

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

        2
  •  3
  •   Denis Zavedeev Brian Kelly    6 年前

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

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

    测试:

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

    输出:

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