Jackson @JsonView, @JsonFilter et Spring

Peut-on utiliser le Jackson @JsonView et @JsonFilter annotations pour modifier le JSON retourné par un contrôleur à ressort MVC, tout en utilisant MappingJacksonHttpMessageConverteret le Printemps @ResponseBody et @RequestBody annotations?

public class Product
{
    private Integer id;
    private Set<ProductDescription> descriptions;
    private BigDecimal price;
    ...
}


public class ProductDescription
{
    private Integer id;
    private Language language;
    private String name;
    private String summary;
    private String lifeStory;
    ...
}

lorsque le client demande une collecte de Products, j'aimerais revenir à une version minimale de chaque ProductDescription, peut-être juste sa carte D'identité. Ensuite, lors d'un appel ultérieur, le client peut utiliser cet ID pour demander une instance complète de ProductDescription avec toutes les propriétés.

Il serait idéal pour pouvoir spécifier ceci sur les méthodes du contrôleur MVC Spring, car la méthode invoquée définit le contexte dans lequel le client demandait les données.

30
demandé sur George Siggouroglou 0000-00-00 00:00:00

5 réponses

Ce problème est résolu!

Suivez

Ajout du support pour Jackson sérialisation vues

Spring MVC supporte maintenant les vues de sérialisation de Jackon pour le rendu différents sous-ensembles d'un même POJO de différents Controllers méthodes (p. ex. page détaillée vs vue sommaire). Numéro: SPR-7156

C'est le SPR-7156.

Statut: Résolu

Description

L'annotation Jsonview de Jackson permet au développeur de contrôler quels aspects d'une méthode sont sérialisés. Avec l'implémentation actuelle, le rédacteur de Jackson view doit être utilisé mais le type de contenu n'est pas disponible. Il serait préférable que dans le cadre de L'annotation RequestBody, un JSONView puisse être spécifié.

Disponible sur printemps ver >= 4.1

UPDATE

Suivre ce lien. Explique avec un exemple l'annotation @JsonView.

13
répondu George Siggouroglou 2015-02-03 15:01:01

finalement, nous voulons utiliser une notation similaire à ce que StaxMan a montré pour JAX-RS. Malheureusement, le printemps ne supporte pas cela, donc nous devons le faire nous-mêmes.

C'est ma solution, c'est pas très joli, mais il fait le travail.

@JsonView(ViewId.class)
@RequestMapping(value="get", method=RequestMethod.GET) // Spring controller annotation
public Pojo getPojo(@RequestValue Long id)
{
   return new Pojo(id);
}

public class JsonViewAwareJsonView extends MappingJacksonJsonView {

    private ObjectMapper objectMapper = new ObjectMapper();

    private boolean prefixJson = false;

    private JsonEncoding encoding = JsonEncoding.UTF8;

    @Override
    public void setPrefixJson(boolean prefixJson) {
        super.setPrefixJson(prefixJson);
        this.prefixJson = prefixJson;
    }

    @Override
    public void setEncoding(JsonEncoding encoding) {
        super.setEncoding(encoding);
        this.encoding = encoding;
    }


    @Override
    public void setObjectMapper(ObjectMapper objectMapper) {
        super.setObjectMapper(objectMapper);
        this.objectMapper = objectMapper;
    }


    @Override
    protected void renderMergedOutputModel(Map<String, Object> model,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        Class<?> jsonView = null;
        if(model.containsKey("json.JsonView")){
            Class<?>[] allJsonViews = (Class<?>[]) model.remove("json.JsonView");
            if(allJsonViews.length == 1)
                jsonView = allJsonViews[0];
        }


        Object value = filterModel(model);
        JsonGenerator generator =
                this.objectMapper.getJsonFactory().createJsonGenerator(response.getOutputStream(), this.encoding);
        if (this.prefixJson) {
            generator.writeRaw("{} && ");
        }
        if(jsonView != null){
            SerializationConfig config = this.objectMapper.getSerializationConfig();
            config = config.withView(jsonView);
            this.objectMapper.writeValue(generator, value, config);
        }
        else
            this.objectMapper.writeValue(generator, value);
    }
}

public class JsonViewInterceptor extends HandlerInterceptorAdapter
{

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
        Object handler, ModelAndView modelAndView) {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        JsonView jsonViewAnnotation = handlerMethod.getMethodAnnotation(JsonView.class);
        if(jsonViewAnnotation != null)
            modelAndView.addObject("json.JsonView", jsonViewAnnotation.value());
    }
}

Au printemps-servlet.xml

<bean name="ViewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
            </map>
        </property>
        <property name="defaultContentType" value="application/json" />
        <property name="defaultViews">
            <list>
                <bean class="com.mycompany.myproject.JsonViewAwareJsonView">
                </bean>
            </list>
        </property>
    </bean>

et

<mvc:interceptors>
    <bean class="com.mycompany.myproject.JsonViewInterceptor" />
</mvc:interceptors>
13
répondu f.khantsis 2012-10-18 22:11:37

je ne sais pas comment les choses fonctionnent avec le Printemps (désolé!), mais Jackson 1.9 peut utiliser @JsonView annotation des méthodes JAX-RS, donc vous pouvez faire:

@JsonView(ViewId.class)
@GET // and other JAX-RS annotations
public Pojo resourceMethod()
{
   return new Pojo();
} 

et Jackson utilisera la vue identifiée par ViewId.la classe de la vue active. Peut-être que le Printemps a (ou aura) des capacités similaires? Avec JAX-RS, cela est géré par le JacksonJaxrsProvider standard, pour ce que cela vaut.

12
répondu StaxMan 2011-10-11 23:47:45

à la recherche de la même réponse, j'ai eu l'idée d'envelopper L'objet responsable d'une vue.

morceau de la classe de contrôleur:

@RequestMapping(value="/{id}", headers="Accept=application/json", method= RequestMethod.GET)
     public  @ResponseBody ResponseBodyWrapper getCompany(HttpServletResponse response, @PathVariable Long id){
        ResponseBodyWrapper responseBody =  new ResponseBodyWrapper(companyService.get(id),Views.Owner.class);
        return responseBody;
     }

public class ResponseBodyWrapper {
private Object object;
private Class<?> view;

public ResponseBodyWrapper(Object object, Class<?> view) {
    this.object = object;
    this.view = view;
}

public Object getObject() {
    return object;
}
public void setObject(Object object) {
    this.object = object;
}
@JsonIgnore
public Class<?> getView() {
    return view;
}
@JsonIgnore
public void setView(Class<?> view) {
    this.view = view;
}

}


Puis-je remplacer writeInternal forme de la méthode MappingJackson2HttpMessageConverter pour vérifier si object to serialize est instanceof wrapper, si c'est le cas, je serialize object avec la vue requise.

public class CustomMappingJackson2 extends MappingJackson2HttpMessageConverter {

private ObjectMapper objectMapper = new ObjectMapper();
private boolean prefixJson;

@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
        throws IOException, HttpMessageNotWritableException {

    JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
    JsonGenerator jsonGenerator =
            this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
    try {
        if (this.prefixJson) {
            jsonGenerator.writeRaw("{} && ");
        }
        if(object instanceof ResponseBodyWrapper){
            ResponseBodyWrapper responseBody = (ResponseBodyWrapper) object;
            this.objectMapper.writerWithView(responseBody.getView()).writeValue(jsonGenerator, responseBody.getObject());
        }else{
            this.objectMapper.writeValue(jsonGenerator, object);
        }
    }
    catch (IOException ex) {
        throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
    }
}

public void setObjectMapper(ObjectMapper objectMapper) {
    Assert.notNull(objectMapper, "ObjectMapper must not be null");
    this.objectMapper = objectMapper;
    super.setObjectMapper(objectMapper);
}

public ObjectMapper getObjectMapper() {
    return this.objectMapper;
}

public void setPrefixJson(boolean prefixJson) {
    this.prefixJson = prefixJson;
    super.setPrefixJson(prefixJson);
}

}

4
répondu user1137146 2012-08-14 21:34:35

la réponse à cela après beaucoup de têtes qui claquent des impasses et des crises de rage de nerd est.... si simple. Dans ce cas d'utilisation, nous avons un Client bean

4
répondu