从 Spring MVC 作为 JSON 发送时,动态忽略来自 Java 对象的字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23101260/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 20:41:47  来源:igfitidea点击:

Ignore fields from Java object dynamically while sending as JSON from Spring MVC

javajsonhibernatespring-mvc

提问by iCode

I have model class like this, for hibernate

我有这样的模型类,用于休眠

@Entity
@Table(name = "user", catalog = "userdb")
@JsonIgnoreProperties(ignoreUnknown = true)
public class User implements java.io.Serializable {

    private Integer userId;
    private String userName;
    private String emailId;
    private String encryptedPwd;
    private String createdBy;
    private String updatedBy;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "UserId", unique = true, nullable = false)
    public Integer getUserId() {
        return this.userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    @Column(name = "UserName", length = 100)
    public String getUserName() {
        return this.userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Column(name = "EmailId", nullable = false, length = 45)
    public String getEmailId() {
        return this.emailId;
    }

    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

    @Column(name = "EncryptedPwd", length = 100)
    public String getEncryptedPwd() {
        return this.encryptedPwd;
    }

    public void setEncryptedPwd(String encryptedPwd) {
        this.encryptedPwd = encryptedPwd;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    @Column(name = "UpdatedBy", length = 100)
    public String getUpdatedBy() {
        return this.updatedBy;
    }

    public void setUpdatedBy(String updatedBy) {
        this.updatedBy = updatedBy;
    }
}

In Spring MVC controller, using DAO, I am able to get the object. and returning as JSON Object.

在 Spring MVC 控制器中,使用 DAO,我能够获取对象。并作为 JSON 对象返回。

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/getUser/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public User getUser(@PathVariable Integer userId) throws Exception {

        User user = userService.get(userId);
        user.setCreatedBy(null);
        user.setUpdatedBy(null);
        return user;
    }
}

View part is done using AngularJS, so it will get JSON like this

视图部分是使用 AngularJS 完成的,所以它会得到这样的 JSON

{
  "userId" :2,
  "userName" : "john",
  "emailId" : "[email protected]",
  "encryptedPwd" : "Co7Fwd1fXYk=",
  "createdBy" : null,
  "updatedBy" : null
}

If I don't want to set encrypted Password, I will set that field also as null.

如果我不想设置加密密码,我也会将该字段设置为空。

But I don't want like this, I dont want to send all fields to client side. If I dont want password, updatedby, createdby fields to send, My result JSON should be like

但我不想这样,我不想将所有字段发送到客户端。如果我不想发送密码、updatedby、createdby 字段,我的结果 JSON 应该像

{
  "userId" :2,
  "userName" : "john",
  "emailId" : "[email protected]"
}

The list of fields which I don't want to send to client coming from other database table. So it will change based on the user who is logged in. How can I do that?

我不想从其他数据库表发送给客户端的字段列表。所以它会根据登录的用户而改变。我该怎么做?

I hope You got my question.

我希望你有我的问题。

采纳答案by user3145373 ツ

Add the @JsonIgnoreProperties("fieldname")annotation to your POJO.

@JsonIgnoreProperties("fieldname")注释添加到您的 POJO。

Or you can use @JsonIgnorebefore the name of the field you want to ignore while deserializing JSON. Example:

或者,您可以@JsonIgnore在反序列化 JSON 时在要忽略的字段名称之前使用。例子:

@JsonIgnore
@JsonProperty(value = "user_password")
public String getUserPassword() {
    return userPassword;
}

GitHub example

GitHub 示例

回答by geoand

Add @JsonInclude(JsonInclude.Include.NON_NULL)(forces Hymanson to serialize null values) to the class as well as @JsonIgnoreto the password field.

添加@JsonInclude(JsonInclude.Include.NON_NULL)(强制 Hymanson 序列化空值)到类以及@JsonIgnore密码字段。

You could of course set @JsonIgnoreon createdBy and updatedBy as well if you always want to ignore then and not just in this specific case.

@JsonIgnore如果您总是想忽略 then 而不仅仅是在这种特定情况下,您当然也可以设置createdBy 和 updatedBy 。

UPDATE

更新

In the event that you do not want to add the annotation to the POJO itself, a great option is Hymanson's Mixin Annotations. Check out the documentation

如果您不想将注释添加到 POJO 本身,那么 Hymanson 的 Mixin Annotations 是一个很好的选择。查看文档

回答by monitorjbl

I know I'm a bit late to the party, but I actually ran into this as well a few months back. All of the available solutions weren't very appealing to me (mixins? ugh!), so I ended up creating a new library to make this process cleaner. It's available here if anyone would like to try it out: https://github.com/monitorjbl/spring-json-view.

我知道我参加聚会有点晚了,但实际上几个月前我也遇到了这个问题。所有可用的解决方案对我来说都不是很有吸引力(mixins?呃!),所以我最终创建了一个新库来使这个过程更干净。如果有人想尝试,它可以在这里找到:https: //github.com/monitorjbl/spring-json-view

The basic usage is pretty simple, you use the JsonViewobject in your controller methods like so:

基本用法非常简单,您可以JsonView在控制器方法中使用该对象,如下所示:

import com.monitorjbl.json.JsonView;
import static com.monitorjbl.json.Match.match;

@RequestMapping(method = RequestMethod.GET, value = "/myObject")
@ResponseBody
public void getMyObjects() {
    //get a list of the objects
    List<MyObject> list = myObjectService.list();

    //exclude expensive field
    JsonView.with(list).onClass(MyObject.class, match().exclude("contains"));
}

You can also use it outside of Spring:

您也可以在 Spring 之外使用它:

import com.fasterxml.Hymanson.databind.ObjectMapper;
import com.fasterxml.Hymanson.databind.module.SimpleModule;
import static com.monitorjbl.json.Match.match;

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(JsonView.class, new JsonViewSerializer());
mapper.registerModule(module);

mapper.writeValueAsString(JsonView.with(list)
      .onClass(MyObject.class, match()
        .exclude("contains"))
      .onClass(MySmallObject.class, match()
        .exclude("id"));

回答by Hamedz

If I were you and wanted to do so, I wouldn't use my User entity in Controller layer.Instead I create and use UserDto (Data transfer object) to communicate with business(Service) layer and Controller. You can use Apache BeanUtils(copyProperties method) to copy data from User entity to UserDto.

如果我是你并且想这样做,我不会在控制器层使用我的用户实体。相反,我创建并使用 UserDto(数据传输对象)与业务(服务)层和控制器进行通信。您可以使用 Apache BeanUtils(copyProperties 方法)将数据从 User 实体复制到 UserDto。

回答by C2dric

I've found a solution for me with Spring and Hymanson

我用 Spring 和 Hymanson 找到了适合我的解决方案

First specify the filter name in the entity

首先在实体中指定过滤器名称

@Entity
@Table(name = "SECTEUR")
@JsonFilter(ModelJsonFilters.SECTEUR_FILTER)
public class Secteur implements Serializable {

/** Serial UID */
private static final long serialVersionUID = 5697181222899184767L;

/**
 * Unique ID
 */
@Id
@JsonView(View.SecteurWithoutChildrens.class)
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@JsonView(View.SecteurWithoutChildrens.class)
@Column(name = "code", nullable = false, length = 35)
private String code;

/**
 * Identifiant du secteur parent
 */
@JsonView(View.SecteurWithoutChildrens.class)
@Column(name = "id_parent")
private Long idParent;

@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "id_parent")
private List<Secteur> secteursEnfants = new ArrayList<>(0);

}

Then you can see the constants filters names class with the default FilterProvider used in spring configuration

然后你可以看到常量过滤器名称类和 spring 配置中使用的默认 FilterProvider

public class ModelJsonFilters {

public final static String SECTEUR_FILTER = "SecteurFilter";
public final static String APPLICATION_FILTER = "ApplicationFilter";
public final static String SERVICE_FILTER = "ServiceFilter";
public final static String UTILISATEUR_FILTER = "UtilisateurFilter";

public static SimpleFilterProvider getDefaultFilters() {
    SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAll();
    return new SimpleFilterProvider().setDefaultFilter(theFilter);
}

}

Spring configuration :

弹簧配置:

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "fr.sodebo")

public class ApiRootConfiguration extends WebMvcConfigurerAdapter {

@Autowired
private EntityManagerFactory entityManagerFactory;


/**
 * config qui permet d'éviter les "Lazy loading Error" au moment de la
 * conversion json par Hymanson pour les retours des services REST<br>
 * on permet à Hymanson d'acceder à sessionFactory pour charger ce dont il a
 * besoin
 */
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

    super.configureMessageConverters(converters);
    MappingHymanson2HttpMessageConverter converter = new MappingHymanson2HttpMessageConverter();
    ObjectMapper mapper = new ObjectMapper();

    // config d'hibernate pour la conversion json
    mapper.registerModule(getConfiguredHibernateModule());//

    // inscrit les filtres json
    subscribeFiltersInMapper(mapper);

    // config du comportement de json views
    mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);

    converter.setObjectMapper(mapper);
    converters.add(converter);
}

/**
 * config d'hibernate pour la conversion json
 * 
 * @return Hibernate5Module
 */
private Hibernate5Module getConfiguredHibernateModule() {
    SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
    Hibernate5Module module = new Hibernate5Module(sessionFactory);
    module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true);

    return module;

}

/**
 * inscrit les filtres json
 * 
 * @param mapper
 */
private void subscribeFiltersInMapper(ObjectMapper mapper) {

    mapper.setFilterProvider(ModelJsonFilters.getDefaultFilters());

}

}

Endly I can specify a specific filter in restConstoller when i need....

最后,我可以在需要时在 restConstoller 中指定一个特定的过滤器......

@RequestMapping(value = "/{id}/droits/", method = RequestMethod.GET)
public MappingHymansonValue getListDroits(@PathVariable long id) {

    LOGGER.debug("Get all droits of user with id {}", id);

    List<Droit> droits = utilisateurService.findDroitsDeUtilisateur(id);

    MappingHymansonValue value;

    UtilisateurWithSecteurs utilisateurWithSecteurs = droitsUtilisateur.fillLists(droits).get(id);

    value = new MappingHymansonValue(utilisateurWithSecteurs);

    FilterProvider filters = ModelJsonFilters.getDefaultFilters().addFilter(ModelJsonFilters.SECTEUR_FILTER, SimpleBeanPropertyFilter.serializeAllExcept("secteursEnfants")).addFilter(ModelJsonFilters.APPLICATION_FILTER,
            SimpleBeanPropertyFilter.serializeAllExcept("services"));

    value.setFilters(filters);
    return value;

}

回答by Jeevan Gowda

In your entity class add @JsonInclude(JsonInclude.Include.NON_NULL)annotation to resolve the problem

在您的实体类中添加@JsonInclude(JsonInclude.Include.NON_NULL)注释以解决问题

it will look like

它看起来像

@Entity
@JsonInclude(JsonInclude.Include.NON_NULL)

回答by Leonardo Beal

Would not creating a UserJsonResponseclass and populating with the wanted fields be a cleaner solution?

创建一个UserJsonResponse类并填充所需的字段不是一个更干净的解决方案吗?

Returning directly a JSON seems a great solution when you want to give all the model back. Otherwise it just gets messy.

当您想返回所有模型时,直接返回 JSON 似乎是一个很好的解决方案。否则只会变得一团糟。

In the future, for example you might want to have a JSON field that does not match any Model field and then you're in a bigger trouble.

将来,例如,您可能希望有一个不匹配任何模型字段的 JSON 字段,然后您会遇到更大的麻烦。

回答by Hett

Can I do it dynamically?

我可以动态地做吗?

Create view class:

创建视图类:

public class View {
    static class Public { }
    static class ExtendedPublic extends Public { }
    static class Internal extends ExtendedPublic { }
}

Annotate you model

注释你的模型

@Document
public class User {

    @Id
    @JsonView(View.Public.class)
    private String id;

    @JsonView(View.Internal.class)
    private String email;

    @JsonView(View.Public.class)
    private String name;

    @JsonView(View.Public.class)
    private Instant createdAt = Instant.now();
    // getters/setters
}

Specify the view class in your controller

在控制器中指定视图类

@RequestMapping("/user/{email}")
public class UserController {

    private final UserRepository userRepository;

    @Autowired
    UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @RequestMapping(method = RequestMethod.GET)
    @JsonView(View.Internal.class)
    public @ResponseBody Optional<User> get(@PathVariable String email) {
        return userRepository.findByEmail(email);
    }

}

Data example:

数据示例:

{"id":"5aa2496df863482dc4da2067","name":"test","createdAt":"2018-03-10T09:35:31.050353800Z"}

回答by Devendra Dora

I have created a JsonUtil which can be used to ignore fields at runtime while giving a response.

我创建了一个 JsonUtil,可用于在运行时忽略字段,同时给出响应。

Example Usage : First argument should be any POJO class (Student) and ignoreFields is comma seperated fields you want to ignore in response.

示例用法:第一个参数应该是任何 POJO 类(学生),而 ignoreFields 是您要在响应中忽略的逗号分隔字段。

 Student st = new Student();
 createJsonIgnoreFields(st,"firstname,age");

import java.util.logging.Logger;

import org.codehaus.Hymanson.map.ObjectMapper;
import org.codehaus.Hymanson.map.ObjectWriter;
import org.codehaus.Hymanson.map.ser.FilterProvider;
import org.codehaus.Hymanson.map.ser.impl.SimpleBeanPropertyFilter;
import org.codehaus.Hymanson.map.ser.impl.SimpleFilterProvider;

public class JsonUtil {

  public static String createJsonIgnoreFields(Object object, String ignoreFields) {
     try {
         ObjectMapper mapper = new ObjectMapper();
         mapper.getSerializationConfig().addMixInAnnotations(Object.class, JsonPropertyFilterMixIn.class);
         String[] ignoreFieldsArray = ignoreFields.split(",");
         FilterProvider filters = new SimpleFilterProvider()
             .addFilter("filter properties by field names",
                 SimpleBeanPropertyFilter.serializeAllExcept(ignoreFieldsArray));
         ObjectWriter writer = mapper.writer().withFilters(filters);
         return writer.writeValueAsString(object);
     } catch (Exception e) {
         //handle exception here
     }
     return "";
   }

   public static String createJson(Object object) {
        try {
         ObjectMapper mapper = new ObjectMapper();
         ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
         return writer.writeValueAsString(object);
        }catch (Exception e) {
         //handle exception here
        }
        return "";
   }
 }    

回答by Shafqat Shafi

Yes, you can specify which fields are serialized as JSON response and which to ignore. This is what you need to do to implement Dynamically ignore properties.

是的,您可以指定将哪些字段序列化为 JSON 响应以及忽略哪些字段。这是实现动态忽略属性所需的操作。

1) First, you need to add @JsonFilter from com.fasterxml.Hymanson.annotation.JsonFilter on your entity class as.

1)首先,您需要在实体类上从 com.fasterxml.Hymanson.annotation.JsonFilter 添加 @JsonFilter 作为。

import com.fasterxml.Hymanson.annotation.JsonFilter;

@JsonFilter("SomeBeanFilter")
public class SomeBean {

  private String field1;

  private String field2;

  private String field3;

  // getters/setters
}

2) Then in your controller, you have to add create the MappingHymansonValue object and set filters on it and in the end, you have to return this object.

2)然后在您的控制器中,您必须添加创建 MappingHymansonValue 对象并在其上设置过滤器,最后,您必须返回此对象。

import java.util.Arrays;
import java.util.List;

import org.springframework.http.converter.json.MappingHymansonValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.Hymanson.databind.ser.FilterProvider;
import com.fasterxml.Hymanson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.Hymanson.databind.ser.impl.SimpleFilterProvider;

@RestController
public class FilteringController {

  // Here i want to ignore all properties except field1,field2.
  @GetMapping("/ignoreProperties")
  public MappingHymansonValue retrieveSomeBean() {
    SomeBean someBean = new SomeBean("value1", "value2", "value3");

    SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field1", "field2");

    FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter", filter);

    MappingHymansonValue mapping = new MappingHymansonValue(someBean);

    mapping.setFilters(filters);

    return mapping;
  }
}

This is what you will get in response:

这是您将得到的回应:

{
  field1:"value1",
  field2:"value2"
}

instead of this:

而不是这个:

{
  field1:"value1",
  field2:"value2",
  field3:"value3"
}

Here you can see it ignores other properties(field3 in this case) in response except for property field1 and field2.

在这里您可以看到它在响应中忽略了除属性 field1 和 field2 之外的其他属性(在本例中为 field3)。

Hope this helps.

希望这可以帮助。