0

I have my @RestController, that has his parameters mapped in @ModelAttribute to shorten the list of parameters. However, some of the attributes have to translate to custom objects (i.e. Long -> Instant). My question is there a way to make spring use my setters to set those objects?

Controller mapping

@GetMapping("/v1/my/path")
public Collection<Dto> getAll(@ModelAttribute MyRequest request) {
    return someSevice.doSomething(request);
}

ModelAttribute class

public class MyRequest {
    private Instant startTime;
    private Instant endTime;
    private ZoneId timezone;

    // How can make spring use this setter to set start time?
    private void setStartTimeFromLong(Long startTime) {
        this.startTime = Instant.ofEpochSecond(startTime);
    }
}
Alan Sereb
  • 2,358
  • 2
  • 17
  • 31
  • 1
    [A custom `InitBinder` editor](https://stackoverflow.com/questions/5211323/what-is-the-purpose-of-init-binder-in-spring-mvc) might be the most direct approach here. That said, since this is a `GET`, the usual model is for the "request" to be a set of query parameters, and I suggest examining the existing Spring support for Querydsl as an MVC parameter. – chrylis -cautiouslyoptimistic- Aug 31 '19 at 18:55
  • Maybe there is no way to achieve – 马向峰 Aug 31 '19 at 18:32

2 Answers2

1

You always can use custom deserialization for your objects

Check next link

public class ItemDeserializer extends StdDeserializer<Item> {

    public ItemDeserializer() {
        this(null);
    }

    public ItemDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Item deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        int id = (Integer) ((IntNode) node.get("id")).numberValue();
        String itemName = node.get("itemName").asText();
        int userId = (Integer) ((IntNode) node.get("createdBy")).numberValue();

        return new Item(id, itemName, new User(userId, null));
    }
}
Alan Sereb
  • 2,358
  • 2
  • 17
  • 31
Ihar Sadounikau
  • 741
  • 6
  • 20
0

Implementing your own converter seems to be the best solution for this problem, since there is no way to annotate a setter to be used for populating @ModuleAttribute.

@Component
public class StringToInstantConverter implements Converter<String, Instant> {

    @Override
    public Instant convert(String epochSeconds) {
        return Instant.ofEpochSecond(Long.valueOf(epochSeconds));
    }
}
Alan Sereb
  • 2,358
  • 2
  • 17
  • 31