在SpringMVC的上下文中,如何使用提供同一类的不同JSON表示的Web服务?

时间:2018-05-24 14:40:10

标签: java json spring-mvc spring-restcontroller

我有一个数据类,如下所示:

public class Person {
   private String name;
   private Long code;

   // corresponding getters and setters
}

我想编写两个Web服务,它们提供Person的两个不同的JSON表示。例如,其中一个提供{"name":"foo"}但另一个提供{"name":"foo", "code":"123"}

作为一个更复杂的场景,假设Person有一个引用属性,例如address。地址也有自己的属性,我希望我的两个Web服务都考虑这个属性,但每个属性都以自己的方式执行。

我的SpringMVC视图应该如何?

请注意我是SpringMVC的新手。请在答案旁边给我一个示例代码。

更新1:几天后,所有答案都促使我解决控制器中的问题或通过注释数据类。但我希望在视图中执行此操作,而不再使用java代码。我可以在JSP文件或百万美元模板甚至是.properties文件中进行吗?

更新2:我找到了json-taglib。但不知何故,它被排除在新的升级之外。有没有类似的解决方案?

6 个答案:

答案 0 :(得分:5)

您正在使用Spring-MVC,因此杰克逊负责JSON序列化和反序列化。

在这种情况下,您可以使用@JsonInclude(Include.NON_NULL)在序列化期间忽略空字段。

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

   @JsonInclude(Include.NON_NULL)
   private Long code;

   // corresponding getters and setters
}

如果您的namecodenull,则会将其排除在输出JSON

之外

因此,如果您将code作为null传递,则您的输出JSON将显示为{"name":"foo"}

答案 1 :(得分:4)

您可以使用MappingJacksonValue查找字段的动态过滤。

过滤器允许您序列化符合自定义条件的字段。

您可以在此处查看我的示例代码:

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

答案 2 :(得分:4)

使用Spring MVC创建JSon时,默认情况下,"视图渲染器"是Jackson。不需要使用JSP或其他视图技术。你想要做的是告诉杰克逊如何为特定情况渲染一个物体。有多种选择,但我建议使用预测。 一个例子:

@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;
}

您的应用程序的视图层是投影类(然后放在一个包中,如果您愿意,则放在视图包中)。 Controller可以选择要渲染的视图,或者您可以将结果设置为动态,如示例所示。

关于如何渲染地址的问题可以通过这样的另一个投影来解决:

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

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

答案 3 :(得分:2)

如果您需要通过投影向此模型添加更多信息,请使用Projection以不同方式公开您的json和ResourceProcessor,例如,可能是另一个DB表。

答案 4 :(得分:2)

根据您的使用案例,只需从jsp / js页面调用您选择的控制器...例如让我们说管理员是用户然后调用AdminController,否则调用用户控制器...这可以使用简单的if / else条件来完成...你也可以查看代理设计模式,但这取决于用例< / p>

答案 5 :(得分:2)

我建议你在前端使用 JSON.stringify(value [,replacer [,space]])函数。我在下面给出了一个例子。您可以根据特定视图的要求编写自定义函数。

EG。以下示例忽略空值。这里我编写了editperson函数,它删除了空值。

该函数有两个输入参数,即键和值。根据要删除或更改的键和值编写逻辑。

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

评论您是否有任何问题。