【问题标题】:How to filter the json response returning from spring rest web service如何过滤从 Spring Rest Web 服务返回的 json 响应
【发布时间】:2014-03-24 12:13:28
【问题描述】:

如何过滤从spring rest web服务返回的json响应。

当使用调用 customEvents 时,我只需要输出 eventId 和 Event 名称。当询问特定事件时,我需要 发送事件的完整详细信息。

Class CustomEvent{

 long id;
 String eventName;
 Account createdBy;
 Account modifiedBy;
 ..


}

Class Account{
 long id;
 String fname;
 String lname;
 ....

}


@Controller
public class CustomEventService
{
    @RequestMapping("/customEvents")
    public @ResponseBody List<CustomEvent> getCustomEventSummaries() {}

    @RequestMapping("/customEvents/{eventId}")
    public @ResponseBody CustomEvent getCustomEvent(@PathVariable("eventId") Long eventId) {}
}

我怎样才能实现上述目标?我目前正在使用spring 3.1。 3.1版本是否支持实现以上或更高版本

【问题讨论】:

  • 您是在使用 Jackson 映射到 JSON 与 MappingJacksonHttpMessageConverter 还是 MappingJackson2HttpMessageConverter?
  • getCustomEventSummaries() 返回Map&lt;[ID_TYPE], [NAME_TYPE]&gt; 有什么问题?或者将CustomEvent 的所有其他属性设为null
  • 我正在使用 MappingJackson2HttpMessageConverter
  • 刚刚找到这个链接?它是一个通用实现“martypitt.wordpress.com/2012/11/05/…
  • 这似乎是一个通用的实现,可以应用于任何控制器和响应。

标签: spring spring-mvc jackson


【解决方案1】:

您可以使用@JsonFilter 进行归档。

波乔:

@JsonFilter("myFilter")
public class User {
    ....
}

控制器:

public String getUser(
            @RequestParam(value="id") String id, 
            @RequestParam(value="requiredFields",required=false ) String requiredFields
        ) throws JsonParseException, JsonMappingException, IOException {

    //Get User 
    User user = userService.getUser(id);
    //Start
    ObjectMapper mapper = new ObjectMapper();
    // and then serialize using that filter provider:
    String json="";
    try {
        if (requiredFields!= null) {
            String[] fields = requiredFields.split("\\,");

            FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
            SimpleBeanPropertyFilter.filterOutAllExcept(new HashSet<String>(Arrays
                  .asList(fields))));

            json = mapper.filteredWriter(filters).writeValueAsString(user);//Deprecated 
        } else {
            SimpleFilterProvider fp = new SimpleFilterProvider().setFailOnUnknownId(false);
            mapper.setFilters(fp);
            json =mapper.writeValueAsString(user);
        }
    } catch (JsonGenerationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (JsonMappingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return json;
}

获取网址:.....&requiredFields=id,Name,Age

【讨论】:

    【解决方案2】:

    我可以想到两种解决方案,它们都利用了 Jackson 的 mixin 功能。

    this 链接中描述了第一个解决方案,该解决方案要复杂得多,但如果您所描述的内容将在代码的其他部分中复制,那么它是一种很棒的方法。 发生的情况是,您定义了一个应用您在 JsonFilter 注释中设置的特定 mixin(在您的情况下为 CustomEventMixin)的方面。

    第二种解决方案要简单得多,包括自己使用jackson对象映射器(而不是将这个责任委托给String),如下面的代码

    @Controller
    public class EventController {
    
    
        private ObjectMapper objectMapper = new ObjectMapper();
    
        public EventController(ObjectMapper objectMapper) {
            this.objectMapper = objectMapper;
            objectMapper.addMixInAnnotations(CustomEvent.class, CustomEventMixin.class);
        }
    
        @RequestMapping("/customEvents")
        @ResponseBody
        public String suggest()  {
            return objectMapper.writeValueAsString(getCustomEvents(), new TypeReference<List<CustomEvent>>() {});
        }
    }
    

    在这两种情况下,您都需要根据 Jackson 规则定义 CustomEventMixin

    更新

    一个示例 Mixin 类是(假设你想忽略 id)

    public interface CustomEventMixin {
    
        String name;
    
        @JsonIgnore
        String id;
    }
    

    【讨论】:

    • 如何创建 CustomEventMixin 类
    • @JsonIgnore 就是这样。史诗!只需在正确的位置用一行代码修复我的完整 REST 设置。
    【解决方案3】:

    您可以为此目的使用@JsonView 注解。但不幸的是,它仅适用于 4.x 及更高版本。

    这是我所知道的最干净的方式:

    public class View {
        public interface Summary {}
        public interface Details extends Summary{}
    }
    
    Class CustomEvent{
        @JsonView(View.Summary.class)
        long id;
    
        @JsonView(View.Summary.class)
        String eventName;
    
        @JsonView(View.Details.class)
        Account createdBy;
    
        @JsonView(View.Details.class)
        Account modifiedBy;
    }
    
    @Controller
    public class CustomEventService
    {
        @JsonView(View.Summary.class)
        @RequestMapping("/customEvents")
        public @ResponseBody List<CustomEvent> getCustomEventSummaries() {}
    
        @RequestMapping("/customEvents/{eventId}")
        public @ResponseBody CustomEvent getCustomEvent(@PathVariable("eventId") Long eventId) {}
    }
    

    请记住,默认情况下,没有 @JsonView 注释的字段也会被序列化。这就是为什么您需要对它们全部进行注释。

    更多信息请阅读:https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

    【讨论】:

      【解决方案4】:

      我对现有项目有类似的要求。由于多个控制器使用单个对象,因此引入视图很痛苦。实施过滤器也不是一个干净的解决方案。因此,我决定在将这些值从 Controller 中的 DTO 中清除之前,然后再将其发送给客户端。所以我自己的几个方法(可能需要更多的执行时间)来解决这个问题。

      public static void includeFields(Object object, String... includeFields) {
              for (PropertyDescriptor propertyDescriptor : PropertyUtils.getPropertyDescriptors(object)) {
                  if (!Arrays.asList(includeFields).contains(propertyDescriptor.getName())) {
                      clearValues(object, propertyDescriptor);
                  }
              }
          }
      
          public static void excludeFields(Object object, String... includeFields) {
              for (PropertyDescriptor propertyDescriptor : PropertyUtils.getPropertyDescriptors(object)) {
                  if (Arrays.asList(includeFields).contains(propertyDescriptor.getName())) {
                      clearValues(object, propertyDescriptor);
                  }
              }
          }
      
          private static void clearValues(Object object, PropertyDescriptor propertyDescriptor) {
              try {
                  if(propertyDescriptor.getPropertyType().equals(boolean.class)) {
                      PropertyUtils.setProperty(object, propertyDescriptor.getName(), false);
                  } else {
                      PropertyUtils.setProperty(object, propertyDescriptor.getName(), null);
                  }
              } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                  //TODO
                  e.printStackTrace();
              }
          }
      

      缺点是boolean 字段将始终具有值,因此将出现在有效负载中。至少这有助于我提供动态解决方案并减少有效负载中的许多字段。

      【讨论】:

      • PropertyUtils 在哪里?
      • 班级是org.apache.commons.beanutils.PropertyUtils,你可以在commons-beanutils.jar找到它
      猜你喜欢
      • 2019-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-25
      • 2017-10-18
      • 2014-09-11
      • 2012-07-11
      • 1970-01-01
      相关资源
      最近更新 更多