【问题标题】:In the context of SpringMVC, how to have web services that provide different JSON representation of a same class?在 SpringMVC 的上下文中,如何让 Web 服务为同一个类提供不同的 JSON 表示?
【发布时间】:2018-11-03 20:16:34
【问题描述】:

我有一个数据类,像这样:

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

   // corresponding getters and setters
}

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

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

我的 SpringMVC 视图应该是什么样的?

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

更新 1: 几天后,所有答案都促使我解决控制器中的问题或通过注释数据类。但我想在视图中做到这一点,没有更多的 java 代码。我可以在 JSP 文件或 thymeleaf 模板甚至 .properties 文件中执行此操作吗?

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

【问题讨论】:

  • 使用单个业务对象模型和两个数据传输对象模型。
  • 我想以软和可配置的方式解决这个问题,比如 JSP 模板或 .properties 文件。另外,假设我想要有很多不同的表示。我真的应该为这个简单的问题写几十个课吗?
  • 你能提供用例吗??
  • 什么意思?我在问题中给出了例子。还不够吗?
  • 我不明白。你说你想写两个控制器,但现在你说你想用jsp写它。您没有提到何时要在相同的提交或不同的提交按钮或不同的 jsp 上提供与视图方式相关的不同输出。任何方式,据我了解,我可以告诉你的是从控制器返回相同的人对象,该对象将由 spring 转换为 json,然后根据需要在前端使用它。编辑您的问题,使其完全清楚。

标签: java json spring-mvc spring-restcontroller


【解决方案1】:

您正在使用Spring-MVC,因此Jackson 负责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"}

【讨论】:

  • 我修改了问题。请阅读更新。我不想做任何java代码。我认为 Spring MVC 在视图中提供了这个功能。
【解决方案2】:

您可以使用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;
    }
}

【讨论】:

  • 对不起,我没有得到你。我无法确定您的答案比@Mạnh 的更多。老实说,您的答案似乎需要更多的 java 代码,而我不喜欢编写 java 代码。你介意解释一下吗?
【解决方案3】:

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

【讨论】:

  • 感谢您的关注,但我想没有如愿以偿。当然这是我的错,因为我无法解释得那么好。简而言之,我想解决表示层的问题,更准确地说是在视图中(我的意思是 jsp、thymeleaf 等)。如果我理解正确的话,你们坚持要在业务模型中或通过注释数据类来解决问题。真的没有办法让它出现在视图中吗?
【解决方案4】:

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

【讨论】:

  • 你回答我的问题了吗? ;)
  • @vahidreza 因此,根据您的模型类,您可以将值分配给 Setter 方法或不相对于控制器,例如在管理控制器中,您将设置为“{“name”:“foo”、“code”:“123”}。”和用户控制器,您只能为 {"name":"foo"} 设置将使用相同的模型类。我希望我做到了:P
  • 我在开玩笑。 ;) 你说得对。当然你还是说我要写java代码,那不是我想要的。
  • 每个人都建议在 Java 端执行此操作的原因是因为您可以控制正在发送的数据....即使您在表示层 (JSP) 执行此操作,您仍然会暴露用户的 JSON 对象,可以使用浏览器上的调试器轻松查看,为此您必须对 JSON 进行编码/解码,在 Java 端执行此操作更安全。但我希望你能找到在 JSP 端执行此操作的解决方案。干杯:)
【解决方案5】:

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

您的应用程序的视图层是投影类(然后放在一个包中,如果您愿意,可以放在视图包中)。 控制器可以选择要渲染的视图,或者您可以像示例中那样使结果动态化。

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

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

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

【讨论】:

    【解决方案6】:

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

    例如。下面的示例忽略空值。在这里,我编写了删除空值的 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

    如果您有任何问题,请发表评论。

    【讨论】:

    • 对不起,您的回答没有意义。我能理解我的愿望和你的建议之间的关系。
    • 如您所见,没有人能够理解您的建议。您可以添加代码以获得更清晰的信息。尽管我对 Spring MVC 非常熟悉,但我仍然不明白您要清楚地提出什么建议。
    • 感谢您花时间和努力。但我不得不说几句。或许一开始,还不清楚。但目前,我认为它已经很清楚了,逐个版本,谢谢评论者。其他答案也是正确的,但与我的愿望略有不同。但是你的答案不是。我很抱歉以这种方式告诉它,但这是现实。你的回答真的没有道理。我应该在前端解决问题吗? JSON.stringify 与我的问题有关吗?!我想你没有得到这个问题。请仔细阅读,拜托!
    • 所以让我们弄清楚这一点。告诉我你的两个网络服务是否有两个不同的请求映射。
    • RequestMapping 没什么大不了的。我想知道如何将我的 java 类序列化为 json,以便每个类的序列化依赖于 web 服务。例如,就像您希望在相册应用程序中对照片列表有两个不同视图的情况一样。在本例中,您编写了两个 jsp 视图。其中一个只显示每张照片的名称和缩略图,而另一个显示名称、日期、创建者等信息。
    猜你喜欢
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 2018-03-20
    • 2015-07-31
    相关资源
    最近更新 更多