2012-05-10 10 views
12

en Java cuando se utiliza la anotaciónJAX-RS JSON de salida bastante

@Produces("application/json") 

la salida no está formateada en formato legible por humanos. ¿Cómo logro eso?

+0

lo JSON-serializador está usando? –

+0

Simplemente usando el estándar netbeans. Creado con el asistente "nuevos servicios web RESTful de Entity Classes" Soy nuevo, pero creo que es jackson? – niklas

+1

Sí, es Jackson – DaTroop

Respuesta

13

Crea esta clase en cualquier lugar de tu proyecto. Se cargará en la implementación. Observe el .configure(SerializationConfig.Feature.INDENT_OUTPUT, true); que configura el asignador para formatear la salida.

Para Jackson 2.0 y posterior, reemplace las dos líneas .configure() con éstos: .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false) .configure(SerializationFeature.INDENT_OUTPUT, true);

y cambiar su importación en consecuencia.

package com.secret; 

import javax.ws.rs.Produces; 
import javax.ws.rs.core.MediaType; 
import javax.ws.rs.ext.ContextResolver; 
import javax.ws.rs.ext.Provider; 
import org.codehaus.jackson.map.DeserializationConfig; 
import org.codehaus.jackson.map.ObjectMapper; 
import org.codehaus.jackson.map.SerializationConfig; 

/** 
* 
* @author secret 
*/ 
@Provider 
@Produces(MediaType.APPLICATION_JSON) 
public class JacksonContextResolver implements ContextResolver<ObjectMapper> { 
    private ObjectMapper objectMapper; 

    public JacksonContextResolver() throws Exception { 
     this.objectMapper = new ObjectMapper(); 
    this.objectMapper 
     .configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false) 
     .configure(SerializationConfig.Feature.INDENT_OUTPUT, true); 
    } 

    @Override 
    public ObjectMapper getContext(Class<?> objectType) { 
     return objectMapper; 
    } 
} 

Tenga en cuenta que el formateo tiene un efecto negativo en el rendimiento.

+2

¿Cómo sabe Jersey utilizar este solucionador de contexto, en lugar del proveedor JAXRS que está incorporado a Jackson? – Cheeso

+0

con la anotación @Provider – DaTroop

+1

Es de suponer que ambos están anotados de esa manera ... – Cheeso

17

Solo para el registro, si desea habilitar la salida bonita solo para algunos recursos, puede usar the @JacksonFeatures annotation en un método de recursos.

Aquí es ejemplo:

@Produces(MediaType.APPLICATION_JSON) 
@JacksonFeatures(serializationEnable = { SerializationFeature.INDENT_OUTPUT }) 
public Bean resource() { 
    return new Bean(); 
} 
+1

'@ JacksonFeatures' solo está disponible en un método, no en una clase. – NomeN

1

Partiendo de útil respuesta de DaTroop, aquí es otra versión que permite elegir entre JSON optimizado y formato JSON basado en la ausencia o presencia de un parámetro "bonita":

package test; 


import javax.ws.rs.Produces; 
import javax.ws.rs.core.Context; 
import javax.ws.rs.core.MediaType; 
import javax.ws.rs.core.MultivaluedMap; 
import javax.ws.rs.core.UriInfo; 
import javax.ws.rs.ext.ContextResolver; 
import javax.ws.rs.ext.Provider; 

import org.codehaus.jackson.map.ObjectMapper; 
import org.codehaus.jackson.map.SerializationConfig; 

@Provider 
@Produces(MediaType.APPLICATION_JSON) 
public class JacksonContextResolver implements ContextResolver<ObjectMapper> { 

    private ObjectMapper prettyPrintObjectMapper; 
    private UriInfo uriInfoContext; 

    public JacksonContextResolver(@Context UriInfo uriInfoContext) throws Exception { 
     this.uriInfoContext = uriInfoContext; 

     this.prettyPrintObjectMapper = new ObjectMapper(); 
     this.prettyPrintObjectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); 
    } 

    @Override 
    public ObjectMapper getContext(Class<?> objectType) { 

     try { 
      MultivaluedMap<String, String> queryParameters = uriInfoContext.getQueryParameters(); 
      if(queryParameters.containsKey("pretty")) { 
       return prettyPrintObjectMapper; 
      } 

     } catch(Exception e) { 
      // protect from invalid access to uriInfoContext.getQueryParameters() 
     } 

     return null; // use default mapper 
    } 
} 
+0

Si bien esta es una buena solución, solo funciona * la primera vez * se llama un recurso. Después de eso, el proveedor ha sido registrado y usted está atascado con la impresión bonita (o no). Para una flexibilidad real, elija una solución de filtro. – mvreijn

12

Así es como se puede hacer adecuadamente salida jSON condicional/no bonita basada en la presencia de "bonita" en la cadena de consulta.

Crear una PrettyFilter que implementa ContainerResponseFilter, que se ejecutará en cada petición :

@Provider 
public class PrettyFilter implements ContainerResponseFilter { 

    @Override 
    public void filter(ContainerRequestContext reqCtx, ContainerResponseContext respCtx) throws IOException { 

     UriInfo uriInfo = reqCtx.getUriInfo(); 
     //log.info("prettyFilter: "+uriInfo.getPath()); 

     MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters(); 
     if(queryParameters.containsKey("pretty")) { 
      ObjectWriterInjector.set(new IndentingModifier(true)); 
     } 

    } 

    public static class IndentingModifier extends ObjectWriterModifier { 

     private final boolean indent; 

     public IndentingModifier(boolean indent) { 
      this.indent = indent; 
     } 


     @Override 
     public ObjectWriter modify(EndpointConfigBase<?> endpointConfigBase, MultivaluedMap<String, Object> multivaluedMap, Object o, ObjectWriter objectWriter, JsonGenerator jsonGenerator) throws IOException { 
      if(indent) jsonGenerator.useDefaultPrettyPrinter(); 
      return objectWriter; 
     } 
    } 
} 

Y más o menos eso es todo!

Deberá asegurarse de que esta clase sea utilizada por Jersey mediante el escaneo automático de paquetes o registrada manualmente.

Pasé algunas horas tratando de lograr eso y descubrí que nadie había publicado antes una solución lista para usar.

+0

Esta es la mejor solución, de lejos, me gustaría poder votar dos veces. No olvides usar 'register (PrettyFilter.class)' en tu 'ResourceConfig'. – mvreijn

Cuestiones relacionadas