requestmapping property only jsonignore jsonfilter for ejemplo java json hibernate spring-mvc

java - property - jsonfilter



Ignorar campos del objeto Java dinámicamente mientras se envía como JSON desde Spring MVC (9)

Tengo una clase modelo como esta, para hibernar

@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; } }

En el controlador Spring MVC, usando DAO, puedo obtener el objeto. y regresar como objeto 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; } }

La parte de la vista se hace usando AngularJS, por lo que obtendrá JSON como este

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

Si no quiero configurar la contraseña cifrada, estableceré ese campo también como nulo.

Pero no quiero así, no quiero enviar todos los campos al lado del cliente. Si no quiero contraseña, updatedby, createdby campos para enviar, Mi resultado JSON debe ser como

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

La lista de campos que no deseo enviar al cliente procedente de otra tabla de base de datos. Por lo tanto, cambiará en función del usuario que haya iniciado sesión. ¿Cómo puedo hacer eso?

Espero que tengas mi pregunta.


¿Puedo hacerlo de forma dinámica?

Crear clase de vista:

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

Anota tu modelo

@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 }

Especifique la clase de vista en su controlador

@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); } }

Ejemplo de datos:

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


¿No sería una solución más limpia crear una clase UserJsonResponse y poblar con los campos deseados?

Devolver directamente un JSON parece una gran solución cuando desea devolverle todo el modelo. De lo contrario, se vuelve complicado.

En el futuro, por ejemplo, es posible que desee tener un campo JSON que no coincida con ningún campo de Modelo y entonces tendrá un problema mayor.


Agregue @JsonInclude(JsonInclude.Include.NON_NULL) (obliga a Jackson a serializar valores nulos) a la clase, así como @JsonIgnore al campo de contraseña.

Por supuesto, también puede configurar @JsonIgnore en createdBy y updatedBy si siempre desea ignorar y no solo en este caso específico.

ACTUALIZAR

En caso de que no desee agregar la anotación al POJO en sí, una gran opción son las anotaciones Mixin de Jackson. Mira la documentation


Agregue la @JsonIgnoreProperties("fieldname") a su POJO.

O puede usar @JsonIgnore antes del nombre del campo que desea ignorar mientras deserializa JSON. Ejemplo:

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

Ejemplo de GitHub


Creé un JsonUtil que se puede usar para ignorar campos en tiempo de ejecución mientras se da una respuesta.

Ejemplo de uso: el primer argumento debe ser cualquier clase POJO (Estudiante) e ignorar Campos es una coma separados campos que desea ignorar en respuesta.

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 ""; } }


En su clase de entidad, agregue la @JsonInclude(JsonInclude.Include.NON_NULL) para resolver el problema

se verá como

@Entity @JsonInclude(JsonInclude.Include.NON_NULL)


He encontrado una solución para mí con Spring y Jackson

Primero, especifique el nombre del filtro en la entidad

@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); }

Luego puede ver la clase de nombres de filtros de constantes con el FilterProvider predeterminado utilizado en la configuración de primavera

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); } }

Configuración de primavera:

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

Finalmente puedo especificar un filtro específico en restConstoller cuando lo necesito ...

@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; }


Sé que llegué un poco tarde a la fiesta, pero en realidad me encontré con esto también hace unos meses. Todas las soluciones disponibles no me resultaban muy atractivas (mixins? Ugh), así que terminé creando una nueva biblioteca para que este proceso fuera más limpio. Está disponible aquí si alguien quisiera probarlo: https://github.com/monitorjbl/spring-json-view .

El uso básico es bastante simple, usted usa el objeto JsonView en sus métodos de controlador así:

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")); }

También puedes usarlo fuera 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.writeValueAsString(JsonView.with(list) .onClass(MyObject.class, match() .exclude("contains")) .onClass(MySmallObject.class, match() .exclude("id"));


Si yo fuera tú y quisiera hacerlo, no usaría mi entidad de usuario en la capa de controlador. En cambio, creo y uso UserDto (objeto de transferencia de datos) para comunicarme con la capa de negocios (servicio) y el controlador. Puede usar Apache ConvertUtils para copiar datos de la entidad User a UserDto.