На этом сайте было задано много одинаковых вопросов без удовлетворительных ответов, поэтому я попытаюсь немного сфокусировать этот вопрос, чтобы получить ответ.Частичная JSON-сериализация объектов ответа REST в Spring MVC
Вот цель, я хотел бы мой Spring MVC 4 (с Spring HATEOAS) веб-сервис, чтобы принять следующие запросы и отвечать соответствующим образом сериализованными лиц:
GET http://myapp/api/people
==> [{
"id": 1,
"name": "Joe",
"email": "[email protected]",
"links": [...]
},
{...etc}]
GET http://myapp/api/people?fields=id,email
==> [{
"id": 1,
"email": "[email protected]"
},
{...etc}]
я хотел бы сохранить мой модели предметной области в моих классах контроллеров и сделать фильтрацию поля во время сериализации JSON, поэтому я оборачивать свой ответ в простом классе с единственным полем для хранения полей для использования в фильтрации:
public class RestResponseEnvelope<T> {
private Set<String> fieldSet;
private T entity;
public RestResponseEnvelope(T entity) {
this.entity = entity;
}
public T getEntity() {
return entity;
}
public Set<String> getFieldSet() {
return fieldSet;
}
public void setFieldSet(Set<String> fieldSet) {
this.fieldSet = fieldSet;
}
public void setFields(String fields) {
Set<String> fieldSet = new HashSet<>();
if (fields != null) {
for (String field : fields.split(",")) {
fieldSet.add(field);
}
}
this.fieldSet = fieldSet;
}
}
так что мой контроллер методы что-то вроде этого:
@RequestMapping(value = "", method = RequestMethod.GET)
public HttpEntity<RestResponseEnvelope<Resources<<Resource<Person>>>> findAllPeople(
@RequestParam(value = "fields", required = false) String fields
){
List<Resource<Person>> peopleResourceList = new ArrayList<>();
for (Person person: personService.findAllPeople()){
Resource<Person> resource = new Resource<>(person);
resource.add(link...);
peopleResourceList.add(resource);
}
Resources<Resource<Person>> resources = new Resources<>(personResourceList);
resources.add(link...);
RestResponseEnvelope<Resources<Resource<Person>>> responseEnvelope = new RestResponseEnvelope<>(resources);
responseEnvelope.setFields(fields);
return new ResponseEntity<>(responseEnvelope, HttpStatus.OK);
}
Чтобы выполнить фильтрацию, я попытался создать собственный HttpMessageConverter
путем расширения Джексона MappingJackson2HttpMessageConverter
так, что он идентифицирует RestResponseEnvelope
объекты и использует их fieldList
применять filterOutAllExcept
фильтр:
@Component
public class FilteringJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
private boolean prefixJson = false;
@Override
public void setPrefixJson(boolean prefixJson) {
this.prefixJson = prefixJson;
super.setPrefixJson(prefixJson);
}
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
ObjectMapper objectMapper = getObjectMapper();
JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(outputMessage.getBody());
try {
if (this.prefixJson) {
jsonGenerator.writeRaw("{} && ");
}
if (object instanceof RestResponseEnvelope){
Set<String> fieldSet = ((RestResponseEnvelope) object).getFieldSet();
Object entity = ((RestResponseEnvelope) object).getEntity();
if (fieldSet != null && !fieldSet.isEmpty()) {
objectMapper.addMixInAnnotations(CellLine.class, PropertyFilterMixin.class);
FilterProvider filters = new SimpleFilterProvider()
.addFilter("filterPropertiesByName",
SimpleBeanPropertyFilter.filterOutAllExcept(fieldSet));
objectMapper.setFilters(filters);
}
objectMapper.writeValue(jsonGenerator, entity);
} else {
objectMapper.writeValue(jsonGenerator, object);
}
} catch (JsonProcessingException e){
throw new HttpMessageNotWritableException("Could not write JSON: " + e.getMessage());
}
}
}
@JsonFilter("filterPropertiesByName")
class PropertyFilterMixin { }
Я играл с этой настройкой и имел смешанные результаты. Когда запрос на отфильтрованные поля работает правильно, последующий запрос для всех полей возвращает объекты, отфильтрованные снова. В других случаях фильтрация не работает или просто отфильтровывает все.
Я хотел был бы иметь возможность взять список запрошенных полей и возвратить сериализованные объекты только с оставшимися полями, в идеале без необходимости выполнять микширование аннотаций, но я полностью зациклен на том, как это сделать. Будет ли моя схема фильтрации корректно оставить поля вложенных атрибутов, когда сериализованный объект верхнего уровня является оберткой или объектами Resource
? Можно ли получить параметры запроса из контекста процесса сериализации JSON, чтобы я мог покончить с классом-оболочкой?