0

I have 2 set of rest APIs implemented using Spring boot using same Java POJO class. I have to return different fields and with different name for few fields, based on API. Presently I am using @JsonView for one set of API. But I also need to give different name for that @JsonView. Ex: Field 'host' needs to be named as 'ip' for a @JsonView, where as 'host' for other API. I am not sure how to map different property names as per @JsonView.

I checked some results like using MixIns. But not sure how to do in Spring Boot Rest APIs, especially at method level.

public class Con {

@JsonView(View.ConInfo.class)
private String host;

private String name;

}

Controller Method:

@JsonView(View.ConInfo.class)
@GetMapping
public Con getConInfo()  {}

@GetMapping("/raw")
public Con getCon()  {}

Expected: {"ip":"10xxx"} for getConInfo API

{"host":"10xxx", "name":"con1"} for getCon API.

Actual: Getting {"host":"10xxx"} for getConInfo API

A MJ
  • 115
  • 1
  • 9

2 Answers2

2

Surely there's a way to achieve what you want (see details below). But I encourage you to avoid it.

As you have different payloads, you should have different classes for representing each payload.


If you still want to follow your original path, you could define two views:

public interface Views {

    interface Summary { }

    interface Details { }
}

And then annotate your payload as follows:

@Data
public class Payload {

    @JsonIgnore
    private String host;

    @JsonView(Views.Details.class)
    private String name;

    @JsonView(Views.Summary.class)
    public String getIp() {
        return host;
    }

    @JsonView(Views.Details.class)
    public String getHost() {
        return host;
    }
}

Now consider the following method, annotated with @JsonView(Views.Summary.class)

@JsonView(Views.Summary.class)
@GetMapping(path = "/summary", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Payload> getSummary() {

    Payload payload = new Payload();
    payload.setHost("0.0.0.0");
    payload.setName("example");

    return ResponseEntity.ok(payload);
}

It will produce:

{
  "ip": "0.0.0.0"
}

Now consider the following methid annotated with @JsonView(Views.Details.class):

@JsonView(Views.Details.class)
@GetMapping(path = "/details", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Payload> getDetails() {

    Payload payload = new Payload();
    payload.setHost("0.0.0.0");
    payload.setName("example");

    return ResponseEntity.ok(payload);
}

It will produce:

{
  "host": "0.0.0.0",
  "name": "example"
}
cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • Thanks! This might do the trick ; using different gettters for each view. – A MJ Oct 03 '19 at 14:21
  • @AMJ While it works, I strongly advise you to use different models for each payload. – cassiomolin Oct 03 '19 at 14:23
  • Okay. Here actually I have entity i.e. db class, which gets loaded with data. Do you suggest to use separate POJOs for the payloads and transform data? But, is it correct design to transform large data, if my list is big. Ex: I need to return a big list of connections, then I need to convert each one. What you suggest in that case? – A MJ Oct 03 '19 at 14:29
  • 1
    @AMJ I still suggest the use of different models. To map your objects, you may look into [MapStruct](https://mapstruct.org/). – cassiomolin Oct 03 '19 at 14:34
1

Based on your description of the problem, you are doing it wrong and, perhaps, do not actually know what you want.

  1. You have two different JSON structures.
  2. You want to send different JSON based on the method that returns the JSON.
  3. Each method will return one JSON structure.
  4. You have data that "maps" to the two different structures.

It seems that you already have data that is represented in one class (perhaps used to access the database); I will call this the VO class Try this:

  1. Create a class for each JSON structure (two different classes, one with hostname and one with IP).
  2. Use Jackson to represent this class in JSON (you are doing this).
  3. Create a mapper to take the data from the VO and create the JSON class of choice. This can be as simple as DataMapper.makeHostnameJsonClass and DataMapper.makeIPJsonClass.
  4. Use the mapper in the web handler to return the correct JSON class.
  5. Declare each web handler to return the correct JSON class.
DwB
  • 37,124
  • 11
  • 56
  • 82
  • Yes, you are right that I am using entity class - which already have the data. I presently did the same, by creating separate json class for one set of APIs. But, somehow I thought the solution was not correct. Also, if the data is huge like if you returning a big list of hosts, I think the transformation logic will slow down and not correct design. – A MJ Oct 03 '19 at 14:20