Игнорировать поля из объекта Java динамически при отправке как JSON из Spring MVC
у меня есть класс модели, как это, для 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;
}
}
весной 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;
}
}
просмотр части делается с помощью AngularJS, так что он получит JSON, как это
{
"userId" :2,
"userName" : "john",
"emailId" : "john@gmail.com",
"encryptedPwd" : "Co7Fwd1fXYk=",
"createdBy" : null,
"updatedBy" : null
}
если я не хочу устанавливать зашифрованный пароль, я также установлю это поле как null.
но я не хочу, я не хочу, чтобы отправить все поля на стороне клиента. Если я не хочу пароль, updatedby, createdby поля для отправки, мой результат JSON должен быть как
{
"userId" :2,
"userName" : "john",
"emailId" : "john@gmail.com"
}
список полей, которые я не хочу отправлять клиенту из другой таблицы базы данных. Поэтому он будет меняться в зависимости от пользователя, вошедшего в систему. Как я могу это сделать?
Я надеюсь, что вы получили мой вопрос.
10 ответов:
добавить
@JsonIgnoreProperties("fieldname")
аннотация к вашему POJO.или вы можете использовать
@JsonIgnore
перед именем поля, которое вы хотите игнорировать при десериализации JSON. Пример:@JsonIgnore @JsonProperty(value = "user_password") public java.lang.String getUserPassword() { return userPassword; }
Я знаю, что немного опоздал на вечеринку, но я действительно столкнулся с этим несколько месяцев назад. Все доступные решения были не очень привлекательны для меня (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")); }
вы также можете использовать его вне весны:
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"));
добавить
@JsonInclude(JsonInclude.Include.NON_NULL)
(заставляет Джексона сериализовать нулевые значения) в класс, а также@JsonIgnore
в поле "Пароль".вы могли бы, конечно, установить
@JsonIgnore
на createdBy и updatedBy, а также если вы всегда хотите игнорировать тогда, а не только в этом конкретном случае.обновление
в случае, если вы не хотите добавлять аннотацию к самому POJO, отличным вариантом являются аннотации Джексона Mixin. Проверьте документация
Если бы я был вами и хотел это сделать, я бы не использовал свой пользовательский объект на уровне контроллера.Вместо этого я создаю и использую UserDto (объект передачи данных) для связи с бизнес-уровнем(сервисом) и контроллером. Вы можете использовать Apache ConvertUtils для копирования данных из объекта User в UserDto.
Я нашел решение для меня с 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); }
затем вы можете увидеть класс имен фильтров констант с фильтром по умолчанию FilterProvider, используемым в конфигурации spring
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 :
@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; }
могу ли я сделать это динамически?
создать класс вид:
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"}
Я создал JsonUtil, который можно использовать для игнорирования полей во время выполнения при предоставлении ответа.
Пример Использования : Первый аргумент должен быть любым классом POJO (Student), а 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 ""; } }
Да, вы можете указать, какие поля сериализуются как ответ JSON, а какие следует игнорировать. Это то, что вам нужно сделать, чтобы реализовать динамически игнорировать свойства.
1) Во-первых, вам нужно добавить @JsonFilter из com.быстрее!Джексон.аннотация.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" }
здесь вы можете видеть, что он игнорирует другие свойства (field3 в этом случае) в ответ, за исключением свойства field1 и field2.
надеюсь, что это помогает.
не создает
UserJsonResponse
класс и заполнение нужных полей будет более чистым решением?возврат непосредственно JSON кажется отличным решением, когда вы хотите вернуть всю модель. В противном случае он просто становится грязным.
в будущем, например, вы можете захотеть иметь поле JSON, которое не соответствует ни одному полю модели, и тогда у вас будут большие проблемы.