In the context of SpringMVC, how to have web services that provide different JSON representation of a same class?

我与影子孤独终老i 提交于 2019-12-05 06:33:48

You're using Spring-MVC so Jackson is in charge of JSON serialize and deserializing.

In this case, you can use @JsonInclude(Include.NON_NULL) to ignore null field during serialization.

public class Person {
   @JsonInclude(Include.NON_NULL)
   private String name;

   @JsonInclude(Include.NON_NULL)
   private Long code;

   // corresponding getters and setters
}

If your name or code is null then it is excluded from output JSON

So if you pass code as null, your ouput JSON will look like {"name":"foo"}

When creating JSon with Spring MVC the "view renderer", by default, is Jackson. There is no need to use things like JSP or other view technology. What you want to do, is to tell Jackson how to render an object for a given situation. Multiple options are available, but I would suggest to use projections. An example:

@RestController
@RequestMapping(value = "person")
public class PersonController {

    private final ProjectionFactory projectionFactory;

    public PersonController(ProjectionFactory projectionFactory) {
        this.projectionFactory = projectionFactory;
    }

    @GetMapping("...")
    public PersonBase getPerson(..., @RequestParam(value = "view", required = false, defaultValue = "base") String view) {
        ...

        if(view.equals("extended")) {
            return projectionFactory.createProjection(PersonExtended.class, person);
        } else {
            return projectionFactory.createProjection(PersonBase.class, person);
        }
    }
}



public interface PersonBase {
    String getName();
}

public interface PersonExtended extends PersonBase {
    Long getCode;
}

The view layer of your application are the projection classes (put then in one package, the view package if you wish). A Controller can choose what view to render, or you could make the result dynamic as in the example.

Your question on how to render the address could be solved with another projection like this:

public interface PersonWithAddress extends PersonExtended {
    AddressProjection getAddress();
}

public interface AddressProjection {
    String getStreetName();
    String getZipcode();
    ...
}
Mafuj Shikder

You can look for dynamic filtering of fields using MappingJacksonValue.

The filter allows you to serialize fields that meet custom criteria.

You can check my sample code here:

package com.github.tddiaz.jsonresponsefiltering;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import jdk.nashorn.internal.objects.annotations.Getter;
import lombok.Data;
import lombok.NonNull;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RestController
    class Controller {

        @GetMapping("/filter")
        public ResponseEntity filter() {
            final Response response = new Response("value1", "value2", "value3");

            //ignore field3; will only return values of field1 and field2
            final SimpleBeanPropertyFilter beanPropertyFilter = SimpleBeanPropertyFilter.filterOutAllExcept("field1", "field2");

            final FilterProvider filterProvider = new SimpleFilterProvider().addFilter("responseFilter", beanPropertyFilter);

            final MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(response);
            mappingJacksonValue.setFilters(filterProvider);

            return ResponseEntity.ok(mappingJacksonValue);
        }
    }

    @JsonFilter("responseFilter")
    @Data
    class Response {
        @NonNull
        private String field1, field2, field3;
    }
}

Use Projection for expose in diferent manners your json, and ResourceProcessor if you need to add more info to this model through projection, e.g another DB table perhaps.

Based on your use case, just call the controller of your choice from the jsp/js page ...For e.g. Let's say Admin is the user then call AdminController else call User Controller ...this can be done using a simple if/else condition...you can also look into Proxy Design Pattern but that depends on the use case

I recommend you to use JSON.stringify(value[, replacer[, space]]) function on frontend. I have given an example below. You have a write a custom function according to your requirements on the specific view.

Eg. The below example ignores null values. Here I have written editperson function which removes null values.

The function has two input parameters i.e. key and value. Write your logic according to the keys and values which you want to remove or change.

var springperson = { "name":"foo","code":null }

console.log(springperson); // person recieved from  spring


function editjson(key, value){
if (value !== null) return value
}

var editperson = JSON.stringify(springperson, editjson); // String representation of person

var personjson=JSON.parse(editperson);  // JSON object representation of person

console.log(personjson); // person as required by the view

Comment if you have any issues.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!