Ignorer dynamicment les champs de l’object Java lors de l’envoi en tant que JSON depuis Spring MVC

J’ai classe de modèle comme ça, pour hibernate

@Entity @Table(name = "user", catalog = "userdb") @JsonIgnoreProperties(ignoreUnknown = true) public class User implements java.io.Serializable { private Integer userId; private Ssortingng userName; private Ssortingng emailId; private Ssortingng encryptedPwd; private Ssortingng createdBy; private Ssortingng 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 Ssortingng getUserName() { return this.userName; } public void setUserName(Ssortingng userName) { this.userName = userName; } @Column(name = "EmailId", nullable = false, length = 45) public Ssortingng getEmailId() { return this.emailId; } public void setEmailId(Ssortingng emailId) { this.emailId = emailId; } @Column(name = "EncryptedPwd", length = 100) public Ssortingng getEncryptedPwd() { return this.encryptedPwd; } public void setEncryptedPwd(Ssortingng encryptedPwd) { this.encryptedPwd = encryptedPwd; } public void setCreatedBy(Ssortingng createdBy) { this.createdBy = createdBy; } @Column(name = "UpdatedBy", length = 100) public Ssortingng getUpdatedBy() { return this.updatedBy; } public void setUpdatedBy(Ssortingng updatedBy) { this.updatedBy = updatedBy; } } 

Dans le contrôleur Spring MVC, en utilisant DAO, je suis capable d’obtenir l’object. et retourner en tant qu’object 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; } } 

Voir la partie est fait en utilisant AngularJS, donc il obtiendra JSON comme ça

 { "userId" :2, "userName" : "john", "emailId" : "john@gmail.com", "encryptedPwd" : "Co7Fwd1fXYk=", "createdBy" : null, "updatedBy" : null } 

Si je ne veux pas définir le mot de passe crypté, je définirai également ce champ comme nul.

Mais je ne veux pas comme ça, je ne veux pas envoyer tous les champs au client. Si je ne veux pas que le mot de passe, mis à jour par, créé par les champs à envoyer, mon résultat JSON devrait être comme

 { "userId" :2, "userName" : "john", "emailId" : "john@gmail.com" } 

La liste des champs que je ne souhaite pas envoyer au client provenant d’une autre table de firebase database. Cela changera donc en fonction de l’utilisateur connecté. Comment puis-je le faire?

J’espère que tu as ma question.

Ajoutez l’ @JsonIgnoreProperties("fieldname") à votre object POJO.

Vous pouvez également utiliser @JsonIgnore avant le nom du champ que vous souhaitez ignorer lors de la désérialisation du JSON. Exemple:

 @JsonIgnore @JsonProperty(value = "user_password") public java.lang.Ssortingng getUserPassword() { return userPassword; } 

Exemple GitHub

Je sais que je suis un peu en retard à la fête, mais je me suis aussi heurté à cela il y a quelques mois. Toutes les solutions disponibles ne m’attiraient pas beaucoup (mixins? Ugh!), Alors j’ai fini par créer une nouvelle bibliothèque pour rendre ce processus plus propre. Il est disponible ici si quelqu’un souhaite l’essayer: https://github.com/monitorjbl/spring-json-view .

L’utilisation de base est assez simple, vous utilisez l’object JsonView dans vos méthodes de contrôleur comme 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 list = myObjectService.list(); //exclude expensive field JsonView.with(list).onClass(MyObject.class, match().exclude("contains")); } 

Vous pouvez également l’utiliser en dehors de 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.writeValueAsSsortingng(JsonView.with(list) .onClass(MyObject.class, match() .exclude("contains")) .onClass(MySmallObject.class, match() .exclude("id")); 

Ajoutez @JsonInclude(JsonInclude.Include.NON_NULL) (force Jackson à sérialiser les valeurs null) à la classe ainsi qu’à @JsonIgnore au champ mot de passe.

Vous pourriez bien sûr définir @JsonIgnore sur createdBy et mettre à jourPar si vous voulez toujours ignorer alors et pas seulement dans ce cas spécifique.

METTRE À JOUR

Dans le cas où vous ne voulez pas append l’annotation au POJO lui-même, une excellente option est les Annotations Mixin de Jackson. Consultez la documentation

Si j’étais vous et que je voulais le faire, je n’utiliserais pas mon entité User dans Controller layer. Au lieu de créer et d’utiliser UserDto (object de transfert de données) pour communiquer avec la couche métier (Service) et le contrôleur. Vous pouvez utiliser Apache ConvertUtils pour copier des données de l’entité utilisateur vers UserDto.

J’ai trouvé une solution avec Spring et Jackson

Spécifiez d’abord le nom du filtre dans l’entité

 @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 Ssortingng 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 secteursEnfants = new ArrayList<>(0); } 

Ensuite, vous pouvez voir la classe des noms de filtres de constantes avec le filtre par défaut utilisé dans la configuration de spring

 public class ModelJsonFilters { public final static Ssortingng SECTEUR_FILTER = "SecteurFilter"; public final static Ssortingng APPLICATION_FILTER = "ApplicationFilter"; public final static Ssortingng SERVICE_FILTER = "ServiceFilter"; public final static Ssortingng UTILISATEUR_FILTER = "UtilisateurFilter"; public static SimpleFilterProvider getDefaultFilters() { SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter.serializeAll(); return new SimpleFilterProvider().setDefaultFilter(theFilter); } } 

Configuration du spring:

 @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
* on permet à jackson d'acceder à sessionFactory pour charger ce dont il a * besoin */ @Override public void configureMessageConverters(List> 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()); } }

Finalement, je peux spécifier un filtre spécifique dans restConstoller quand j’ai besoin de ….

 @RequestMapping(value = "/{id}/droits/", method = RequestMethod.GET) public MappingJacksonValue getListDroits(@PathVariable long id) { LOGGER.debug("Get all droits of user with id {}", id); List 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; } 

Puis-je le faire dynamicment?

Créer une classe de vue:

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

Annoter votre modèle

 @Document public class User { @Id @JsonView(View.Public.class) private Ssortingng id; @JsonView(View.Internal.class) private Ssortingng email; @JsonView(View.Public.class) private Ssortingng name; @JsonView(View.Public.class) private Instant createdAt = Instant.now(); // getters/setters } 

Spécifiez la classe de vue dans votre contrôleur

 @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 get(@PathVariable Ssortingng email) { return userRepository.findByEmail(email); } } 

Exemple de données:

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

J’ai créé un JsonUtil qui peut être utilisé pour ignorer les champs lors de l’exécution tout en donnant une réponse.

Exemple d’utilisation: Le premier argument doit être n’importe quelle classe POJO (Student) et ignoreFields est des champs séparés par des virgules que vous souhaitez ignorer en réponse.

  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 Ssortingng createJsonIgnoreFields(Object object, Ssortingng ignoreFields) { try { ObjectMapper mapper = new ObjectMapper(); mapper.getSerializationConfig().addMixInAnnotations(Object.class, JsonPropertyFilterMixIn.class); Ssortingng[] ignoreFieldsArray = ignoreFields.split(","); FilterProvider filters = new SimpleFilterProvider() .addFilter("filter properties by field names", SimpleBeanPropertyFilter.serializeAllExcept(ignoreFieldsArray)); ObjectWriter writer = mapper.writer().withFilters(filters); return writer.writeValueAsSsortingng(object); } catch (Exception e) { //handle exception here } return ""; } public static Ssortingng createJson(Object object) { try { ObjectMapper mapper = new ObjectMapper(); ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter(); return writer.writeValueAsSsortingng(object); }catch (Exception e) { //handle exception here } return ""; } } 

Oui, vous pouvez spécifier quels champs sont sérialisés en tant que réponse JSON et lesquels doivent être ignorés. C’est ce que vous devez faire pour implémenter Dynamiquement les propriétés.

1) Tout d’abord, vous devez append @JsonFilter à partir de com.fasterxml.jackson.annotation.JsonFilter sur votre classe d’entité.

 import com.fasterxml.jackson.annotation.JsonFilter; @JsonFilter("SomeBeanFilter") public class SomeBean { private Ssortingng field1; private Ssortingng field2; private Ssortingng field3; // getters/setters } 

2) Ensuite, dans votre contrôleur, vous devez append l’object MappingJacksonValue et y placer des filtres et à la fin, vous devez retourner cet object.

 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 resortingeveSomeBean() { 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; } } 

Voici ce que vous obtiendrez en réponse:

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

au lieu de cela:

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

Ici, vous pouvez voir qu’il ignore les autres propriétés (field3 dans ce cas) en réponse, sauf pour les champs field1 et field2.

J’espère que cela t’aides.

La création d’une classe UserJsonResponse et son UserJsonResponse avec les champs souhaités ne seraient-ils pas une solution plus propre?

Renvoyer directement un JSON semble une excellente solution lorsque vous souhaitez redonner tout le modèle. Sinon, ça devient désordonné.

À l’avenir, par exemple, vous voudrez peut-être avoir un champ JSON qui ne correspond à aucun champ Model, et vous rencontrez alors un problème plus grave.

Dans votre classe d’entités, ajoutez l’ @JsonInclude(JsonInclude.Include.NON_NULL) pour résoudre le problème.

ça va ressembler

 @Entity @JsonInclude(JsonInclude.Include.NON_NULL)