简体   繁体   中英

Changing how Swagger generates the JSON response in Java

I use Swagger to set up an REST Api Service. For this I used following versions:

swagger (YAML)-file version 2.0
swagger codegen cli version v2.3.0
springfox version 2.5.0

The task of the API Service is to respond with data that we store in our database, as well as its relations. However, we have circular relations in our data. This means that each Object a can have a relation to Object b, which can have a backward relation to Object a.

Now using Swagger, this will generate an endless long JSON, resulting in an error. The code looks like this:

public class Service extends DataObject {

   @SerializedName("provides")
   private List<Utility> provides;

   /**
    * Get provides
    *
    * @return provides
    **/
   @ApiModelProperty(required = true, value = "")
   public List<Utility> getProvides() {
       return provides;
   }
}

public class Utility extends DataObject {
   @SerializedName("providedBy")
   private List<Service> providedBy;

   /**
    * Get providedBy
    *
    * @return providedBy
    **/
   @ApiModelProperty(required = true, value = "")
   public List<Service> getProvidedBy() {
       return providedBy;
   }
}

Then in the response:

@Override
public ResponseEntity<List<Service>> servicesGet(
        @Min(0) @ApiParam(value = "The number of layers of relations the object is enriched by. Lower numbers typically increase performance.", defaultValue = "0") @Valid @RequestParam(value = "layersOfRelations", required = false, defaultValue = "0") final Integer layersOfRelations) {
    String accept = this.request.getHeader("Accept");
        List<Service> services = Util.getServices();
        return new ResponseEntity<List<Service>>(services, HttpStatus.OK);
}

My Question is, where and how can I change the output that will be automatically generated for the return in the method servicesGet()?
I would like to not transform the whole object b into JSON, but rather only its title, so that there will be no endless recursion.

So after some research I found out, that swagger is using Jackson to convert objects into JSON (even though the @SerializedName parameter contained in the generated code was from the gson library, at least in my case).

This means that in this case it I can simply use customization techniques that Jackson provides, and it worked like a charm. I only had to write a custom Jackson adapter annotation:

@SerializedName("provides")
@JsonSerialize(using = DataObjectAdapter.class)
private List<Utility> provides;

and the adapter looks like this:

public static class DataObjectAdapter extends JsonSerializer<List<Utility>> {

    @Override
    public void serialize(final List<Utility> arg0, final JsonGenerator arg1, final SerializerProvider arg2)
            throws IOException {
        arg1.writeStartArray();
        for (Utility object : arg0) {
            arg1.writeStartObject();
            arg1.writeStringField("id", object.getId());
            arg1.writeStringField("title", object.getTitle());
            arg1.writeStringField("description", object.getDescription());
            arg1.writeEndObject();
        }
        arg1.writeEndArray();
    }

}

So now instead of writing the whole object into JSON (and thus recursivly adding all child objects into it, he will only write the information I described in the adapter into JSON.

Similarly a deserializer, where I read out the ID in the JSON and map it to my data, would look like this

public static class ServiceDeserializer extends StdDeserializer<Service> {

    public ServiceDeserializer() {
        super(Service.class);
    }

    @Override
    public Service deserialize(final JsonParser jp, final DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        List<Utility> objects = Util.getUtilities();
        JsonNode node = jp.getCodec().readTree(jp);
        String id = node.get("id").asText();
        String title = node.get("title").asText();
        String description = node.get("description").asText();
        Service service = new Service(id, title, description);
        for (JsonNode utility : node.get("provides")) {
            String checkId = utility.get("id").asText();
            for (DataObject object : objects) {
                if (object.getId().equals(checkId)) {
                    service.addUtility(object);
                    break;
                }
            }
        }
        return service;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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