在从 Spring MVC 作为 JSON 发送时动态忽略 Java 对象中的字段
Posted
技术标签:
【中文标题】在从 Spring MVC 作为 JSON 发送时动态忽略 Java 对象中的字段【英文标题】:Ignore fields from Java object dynamically while sending as JSON from Spring MVC 【发布时间】:2014-05-30 20:13:54 【问题描述】:我有这样的模型类,用于休眠
@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;
在 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 部分是使用 AngularJS 完成的,所以它会得到这样的 JSON
"userId" :2,
"userName" : "john",
"emailId" : "john@gmail.com",
"encryptedPwd" : "Co7Fwd1fXYk=",
"createdBy" : null,
"updatedBy" : null
如果我不想设置加密密码,我也会将该字段设置为空。
但我不想这样,我不想将所有字段发送到客户端。如果我不想发送密码、updatedby、createdby 字段,我的结果 JSON 应该是这样的
"userId" :2,
"userName" : "john",
"emailId" : "john@gmail.com"
我不想发送给来自其他数据库表的客户端的字段列表。所以它会根据登录的用户而改变。我该怎么做?
我希望你有我的问题。
【问题讨论】:
你对这个答案有什么看法? ***.com/a/30559076/3488143 此信息可能会有所帮助***.com/questions/12505141/… 我在这里回答Easy way to filter fields dynamically 要动态忽略 json 响应中的字段,在您发回响应之前,请尝试将该字段设置为 null,即entityClass.setFieldToIgnore(null)
。 spring-mvc默认设置为反序列化时忽略空值,也可以手动配置
【参考方案1】:
您好,我已经使用 Gson 库实现了动态过滤,如下所示:
JsonObject jsonObj = new Gson().fromJson(mapper.writeValueAsString(sampleObject), JsonObject.class);
jsonObj.remove("someProperty");
String data = new Gson().toJson(jsonObj);
【讨论】:
【参考方案2】:要实现动态过滤,请点击链接 - https://iamvickyav.medium.com/spring-boot-dynamically-ignore-fields-while-converting-java-object-to-json-e8d642088f55
在模型类中添加@JsonFilter("Filter name")注解。
在控制器函数里面添加代码:-
SimpleBeanPropertyFilter simpleBeanPropertyFilter =
SimpleBeanPropertyFilter.serializeAllExcept("id", "dob");
FilterProvider filterProvider = new SimpleFilterProvider()
.addFilter("Filter name", simpleBeanPropertyFilter);
List<User> userList = userService.getAllUsers();
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(userList);
mappingJacksonValue.setFilters(filterProvider);
return mappingJacksonValue;
确保返回类型是 MappingJacksonValue。
【讨论】:
【参考方案3】:将@JsonIgnoreProperties("fieldname")
注释添加到您的 POJO。
或者您可以在反序列化 JSON 时在要忽略的字段名称前使用 @JsonIgnore
。示例:
@JsonIgnore
@JsonProperty(value = "user_password")
public String getUserPassword()
return userPassword;
GitHub example
【讨论】:
我可以动态做吗?不在 POJO 中?我可以在我的 Controller 类中这样做吗? @iProgrammer :这是你想要的类似:***.com/questions/8179986/… @iProgrammer:这里的答案非常令人印象深刻***.com/questions/13764280/… remark:@JsonIgnore
是 com.fasterxml.jackson.annotation.JsonIgnore
不是 org.codehaus.jackson.annotate.JsonIgnore
。
在读取请求和发送响应时都会忽略。我只想在发送响应时忽略,因为我需要请求对象中的该属性。有什么想法吗?【参考方案4】:
如果我是你并且想要这样做,我不会在控制器层中使用我的用户实体。相反,我创建并使用 UserDto(数据传输对象)与业务(服务)层和控制器进行通信。 您可以使用 Apache BeanUtils(copyProperties 方法)将数据从 User 实体复制到 UserDto。
【讨论】:
【参考方案5】:我只使用@kryger 建议的@JsonIgnore
解决了问题。
所以你的吸气剂会变成:
@JsonIgnore
public String getEncryptedPwd()
return this.encryptedPwd;
您当然可以在字段、setter 或 getter 上设置@JsonIgnore
,如here 所述。
而且,如果您只想在序列化方面保护加密密码(例如,当您需要登录用户时),请将此 @JsonProperty
注释添加到您的字段:
@JsonProperty(access = Access.WRITE_ONLY)
private String encryptedPwd;
更多信息here。
【讨论】:
【参考方案6】:将 @JsonIgnore
放在字段或其 getter 上,或创建自定义 dto
@JsonIgnore
private String encryptedPwd;
或如上面提到的ceekay
用@JsonProperty
对其进行注释,其中访问属性设置为只写
@JsonProperty( value = "password", access = JsonProperty.Access.WRITE_ONLY)
private String encryptedPwd;
【讨论】:
【参考方案7】:这是针对上述answer 的干净实用工具:
@GetMapping(value = "/my-url")
public @ResponseBody
MappingJacksonValue getMyBean()
List<MyBean> myBeans = Service.findAll();
MappingJacksonValue mappingValue = MappingFilterUtils.applyFilter(myBeans, MappingFilterUtils.JsonFilterMode.EXCLUDE_FIELD_MODE, "MyFilterName", "myBiggerObject.mySmallerObject.mySmallestObject");
return mappingValue;
//AND THE UTILITY CLASS
public class MappingFilterUtils
public enum JsonFilterMode
INCLUDE_FIELD_MODE, EXCLUDE_FIELD_MODE
public static MappingJacksonValue applyFilter(Object object, final JsonFilterMode mode, final String filterName, final String... fields)
if (fields == null || fields.length == 0)
throw new IllegalArgumentException("You should pass at least one field");
return applyFilter(object, mode, filterName, new HashSet<>(Arrays.asList(fields)));
public static MappingJacksonValue applyFilter(Object object, final JsonFilterMode mode, final String filterName, final Set<String> fields)
if (fields == null || fields.isEmpty())
throw new IllegalArgumentException("You should pass at least one field");
SimpleBeanPropertyFilter filter = null;
switch (mode)
case EXCLUDE_FIELD_MODE:
filter = SimpleBeanPropertyFilter.serializeAllExcept(fields);
break;
case INCLUDE_FIELD_MODE:
filter = SimpleBeanPropertyFilter.filterOutAllExcept(fields);
break;
FilterProvider filters = new SimpleFilterProvider().addFilter(filterName, filter);
MappingJacksonValue mapping = new MappingJacksonValue(object);
mapping.setFilters(filters);
return mapping;
【讨论】:
【参考方案8】:我们可以通过在声明属性时设置对JsonProperty.Access.WRITE_ONLY
的访问权限来做到这一点。
@JsonProperty( value = "password", access = JsonProperty.Access.WRITE_ONLY)
@SerializedName("password")
private String password;
【讨论】:
【参考方案9】:是的,您可以指定将哪些字段序列化为 JSON 响应以及忽略哪些字段。 这是实现动态忽略属性所需要做的。
1) 首先,您需要在您的实体类上添加 com.fasterxml.jackson.annotation.JsonFilter 中的@JsonFilter as。
import com.fasterxml.jackson.annotation.JsonFilter;
@JsonFilter("SomeBeanFilter")
public class SomeBean
private String field1;
private String field2;
private String field3;
// getters/setters
2) 然后在您的控制器中,您必须添加创建 MappingJacksonValue 对象并在其上设置过滤器,最后,您必须返回此对象。
import java.util.Arrays;
import java.util.List;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
@RestController
public class FilteringController
// Here i want to ignore all properties except field1,field2.
@GetMapping("/ignoreProperties")
public MappingJacksonValue retrieveSomeBean()
SomeBean someBean = new SomeBean("value1", "value2", "value3");
SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("field1", "field2");
FilterProvider filters = new SimpleFilterProvider().addFilter("SomeBeanFilter", filter);
MappingJacksonValue mapping = new MappingJacksonValue(someBean);
mapping.setFilters(filters);
return mapping;
这是您将得到的回应:
field1:"value1",
field2:"value2"
而不是这个:
field1:"value1",
field2:"value2",
field3:"value3"
在这里你可以看到它忽略了除了属性 field1 和 field2 之外的其他属性(在本例中为 field3)。
希望这会有所帮助。
【讨论】:
@Shafqat Man,非常感谢你,你是我的救星。花了将近一天的时间试图找出这种功能。这个解决方案如此优雅和简单?并完全按照要求进行。应标记为正确答案。【参考方案10】:在你的实体类中添加@JsonInclude(JsonInclude.Include.NON_NULL)
注解解决问题
看起来像
@Entity
@JsonInclude(JsonInclude.Include.NON_NULL)
【讨论】:
完全不相关的回答。问题的目的不同,而答案是关于其他的。 -1 【参考方案11】:我创建了一个 JsonUtil,可用于在运行时忽略字段,同时给出响应。
示例用法: 第一个参数应该是任何 POJO 类(学生),ignoreFields 是您想要在响应中忽略的逗号分隔字段。
Student st = new Student();
createJsonIgnoreFields(st,"firstname,age");
import java.util.logging.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.map.ser.FilterProvider;
import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter;
import org.codehaus.jackson.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 "";
【讨论】:
【参考方案12】:我可以动态做吗?
创建视图类:
public class View
static class Public
static class ExtendedPublic extends Public
static class Internal extends ExtendedPublic
注释你的模型
@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
在控制器中指定视图类
@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);
数据示例:
"id":"5aa2496df863482dc4da2067","name":"test","createdAt":"2018-03-10T09:35:31.050353800Z"
【讨论】:
这是一个奇妙而简约的答案!我想以 JSON 形式返回 @Configuration 注释组件中的几个字段,并跳过所有自动包含的内部字段。非常感谢!【参考方案13】:创建UserJsonResponse
类并填充所需字段不是更清洁的解决方案吗?
当您想要返回所有模型时,直接返回 JSON 似乎是一个很好的解决方案。否则它只会变得一团糟。
例如,将来您可能希望拥有一个与任何 Model 字段都不匹配的 JSON 字段,那么您就有更大的麻烦了。
【讨论】:
【参考方案14】:我用 Spring 和 jackson 找到了适合我的解决方案
先在实体中指定过滤器名称
@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);
然后你可以看到常量过滤器名称类与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);
弹簧配置:
@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 jackson pour les retours des services REST<br>
* on permet à jackson d'acceder à sessionFactory pour charger ce dont il a
* besoin
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters)
super.configureMessageConverters(converters);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
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());
最后我可以在需要时在 restConstoller 中指定一个特定的过滤器......
@RequestMapping(value = "/id/droits/", method = RequestMethod.GET)
public MappingJacksonValue getListDroits(@PathVariable long id)
LOGGER.debug("Get all droits of user with id ", id);
List<Droit> droits = utilisateurService.findDroitsDeUtilisateur(id);
MappingJacksonValue value;
UtilisateurWithSecteurs utilisateurWithSecteurs = droitsUtilisateur.fillLists(droits).get(id);
value = new MappingJacksonValue(utilisateurWithSecteurs);
FilterProvider filters = ModelJsonFilters.getDefaultFilters().addFilter(ModelJsonFilters.SECTEUR_FILTER, SimpleBeanPropertyFilter.serializeAllExcept("secteursEnfants")).addFilter(ModelJsonFilters.APPLICATION_FILTER,
SimpleBeanPropertyFilter.serializeAllExcept("services"));
value.setFilters(filters);
return value;
【讨论】:
一个简单的问题为什么这么复杂【参考方案15】:我知道我参加聚会有点晚了,但实际上几个月前我也遇到了这个问题。所有可用的解决方案对我来说都不是很吸引人(mixins?呃!),所以我最终创建了一个新库来使这个过程更干净。如果有人想尝试一下,可以在这里找到:https://github.com/monitorjbl/spring-json-view。
基本用法非常简单,您可以在控制器方法中使用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"));
你也可以在 Spring 之外使用它:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.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"));
【讨论】:
谢谢!这就是我要走的路。我需要在不同位置具有相同对象的自定义 JSON 视图,而 @JsonIgnore 就是行不通。这个库让它变得非常简单。 你让我的代码更清晰,实现更容易。谢谢 @monitorjbl :这有点偏离轨道,我使用了 json 视图并解决了我的目的。但是我无法为 java.util.Date 类注册自定义序列化程序(没有运行时/编译时错误),就像我能够注册自定义序列化程序的字符串一样。 JsonView 现在似乎是 Jackson 对象映射器的一部分【参考方案16】:将@JsonInclude(JsonInclude.Include.NON_NULL)
(强制Jackson 序列化空值)添加到类以及@JsonIgnore
到密码字段。
如果您总是想忽略,当然也可以在 createdBy 和 updatedBy 上设置 @JsonIgnore
,而不仅仅是在这种特定情况下。
更新
如果您不想将注释添加到 POJO 本身,一个不错的选择是 Jackson 的 Mixin Annotations。查看documentation
【讨论】:
我可以动态做吗?不在 POJO 中?我可以在我的 Controller 类中这样做吗? 你的意思是不想给POJO添加注解? 因为有时我可能想将所有字段发送到客户端。有时很少有字段。我应该发送到客户端的字段仅从控制器类中的数据库中获取。之后,我只需要设置应该忽略哪些字段。以上是关于在从 Spring MVC 作为 JSON 发送时动态忽略 Java 对象中的字段的主要内容,如果未能解决你的问题,请参考以下文章
接受/返回 XML/JSON 请求和响应 - Spring MVC
POST JSON 失败,出现 415 Unsupported media type, Spring 3 mvc