Как преобразовать тело запроса в @RestController в список абстрактных значений? - PullRequest
0 голосов
/ 12 сентября 2018

Допустим, у нас есть следующие классы:

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

Как я могу связать или преобразовать json в тело запроса List<Investment>, не используя @JsonSubTypes Джексона для абстрактного класса Investment, и вместо этого использовать InvestmentFactory?

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

@ JsonDeserialize отлично работает, но если у вас больше полей, чем просто типа, вам придется установить их все вручную. Если вы хотите вернуться к Джексону, вы можете использовать:

Investment.class

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

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

Прелесть этого в том, что если вы начнете добавлять поля в раздел «Инвестиции», вам не нужно будет добавлять их в десериализаторе (по крайней мере, это случилось со мной в моем случае), но вместо этого Джексон позаботится об этом за вас. , Итак, завтра у вас может быть тестовый набор:

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

Тебе должно быть хорошо идти!

0 голосов
/ 12 сентября 2018

Если вы можете использовать @ 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"]
...