json spring spring-mvc gson

json - gson maven



HttpMessageConverter personalizado con @ResponseBody para hacer cosas Json (8)

Bueno ... fue tan difícil encontrar la respuesta y tuve que seguir tantas pistas sobre la información incompleta que creo que sería bueno publicar la respuesta completa aquí. Por lo tanto, será más fácil para el siguiente buscar esto.

Primero tuve que implementar el HttpMessageConverter personalizado:

package net.iogui.web.spring.converter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; public class GsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> { private Gson gson = new Gson(); public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); public GsonHttpMessageConverter(){ super(new MediaType("application", "json", DEFAULT_CHARSET)); } @Override protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { try{ return gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz); }catch(JsonSyntaxException e){ throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e); } } @Override protected boolean supports(Class<?> clazz) { return true; } @Override protected void writeInternal(Object t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //TODO: adapt this to be able to receive a list of json objects too String json = gson.toJson(t); outputMessage.getBody().write(json.getBytes()); } //TODO: move this to a more appropriated utils class public String convertStreamToString(InputStream is) throws IOException { /* * To convert the InputStream to String we use the Reader.read(char[] * buffer) method. We iterate until the Reader return -1 which means * there''s no more data to read. We use the StringWriter class to * produce the string. */ if (is != null) { Writer writer = new StringWriter(); char[] buffer = new char[1024]; try { Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); int n; while ((n = reader.read(buffer)) != -1) { writer.write(buffer, 0, n); } } finally { is.close(); } return writer.toString(); } else { return ""; } } }

Luego tuve que quitarme la etiqueta impulsada por annnotaion y configurar todo con mis propias manos en el archivo de configuración spring-mvc:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!-- Configures the @Controller programming model --> <!-- To use just with a JSR-303 provider in the classpath <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" /> --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" /> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="webBindingInitializer"> <bean class="net.iogui.web.spring.util.CommonWebBindingInitializer" /> </property> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /> <bean class="org.springframework.http.converter.StringHttpMessageConverter" /> <bean class="org.springframework.http.converter.ResourceHttpMessageConverter" /> <bean class="net.iogui.web.spring.converter.GsonHttpMessageConverter" /> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" /> <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" /> <!-- bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" /--> </list> </property> </bean> <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /> <context:component-scan base-package="net.iogui.teste.web.controller"/> <!-- Forwards requests to the "/" resource to the "login" view --> <mvc:view-controller path="/" view-name="home"/> <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory --> <mvc:resources mapping="/resources/**" location="/resources/" /> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/view/"/> <property name="suffix" value=".jsp"/> </bean> </beans>

Mira, para que el Formateador y el Validador funcionen, también tenemos que crear un webBindingInitializer personalizado:

package net.iogui.web.spring.util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.convert.ConversionService; import org.springframework.validation.Validator; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.support.WebBindingInitializer; import org.springframework.web.context.request.WebRequest; public class CommonWebBindingInitializer implements WebBindingInitializer { @Autowired(required=false) private Validator validator; @Autowired private ConversionService conversionService; @Override public void initBinder(WebDataBinder binder, WebRequest request) { binder.setValidator(validator); binder.setConversionService(conversionService); } }

Una cosa interesante que ver es que para que la configuración funcione sin la etiqueta accionada por anotación , tenemos que configurar manualmente un AnnotationMethodHandlerAdapter y un DefaultAnnotationHandlerMapping . Y para hacer que AnnotationMethodHandlerAdapter sea capaz de manejar el formateo y la validación, tuvimos que configurar un validador , un servicio de conversión y crear un webBindingInitializer personalizado.

Espero que todo esto ayude a alguien más aparte de mí.

En mi búsqueda desesperada, this publicación de @Bozho fue extremadamente útil. También estoy agradecido con @GaryF porque su respuesta me llevó a la this . Para ustedes que están tratando de hacer esto en la primavera 3.1, ver @Robby Pond responder ... Mucho más fácil, ¿no?

No me gusta Jackson.

Quiero usar ajax pero con Google Gson.

Así que estoy tratando de descubrir cómo implementar mi propio HttpMessageConverter para usarlo con la anotación @ResponseBody. ¿Puede alguien tomarse un tiempo para mostrarme el camino que debería tomar? ¿Qué configuraciones debería activar? También me pregunto si puedo hacer esto y seguir usando <mvc: annotation-driven />?

Gracias por adelantado.

Ya lo solicité en Spring Community Foruns hace 3 días sin respuesta, así que pregunto aquí para ver si tengo una mejor oportunidad. Los foros de la comunidad de Spring enlazan a mi pregunta

También hice una búsqueda exhaustiva en la web y encontré algo interesante sobre este tema, pero parece que están pensando en ponerlo en Spring 3.1 y todavía estoy usando la primavera 3.0.5: Mejora de primavera de Jira pregunte

Bueno ... ahora estoy tratando de depurar el código de Spring para averiguar cómo hacerlo, pero tengo algunos problemas, como he dicho aquí: Spring Framework Build Error

Si hay otra manera de hacer esto y me falta, por favor avíseme.


Necesita crear un GsonMessageConverter que extienda AbstractHttpMessageConverter y use la etiqueta m vc-message-converters para registrar su convertidor de mensajes. Esa etiqueta permitirá que tu convertidor tenga prioridad sobre el de Jackson.




Puede hacerlo escribiendo el archivo WebConfig como un archivo Java. Extienda su archivo de configuración con WebMvcConfigurerAdapter y anule el método extendMessageConverters para agregar su Convertidor de mensajes intentado. Este método retendrá los convertidores predeterminados agregados por Spring y agregará su convertidor al final. Aparentemente tienes el control total de la lista y puedes agregar donde quieras en la lista.

@Configuration @EnableWebMvc @ComponentScan(basePackageClasses={WebConfig.class}) public class WebConfig extends WebMvcConfigurerAdapter { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new GsonHttpMessageConverter()); } } package net.iogui.web.spring.converter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.nio.charset.Charset; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; public class GsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> { private Gson gson = new Gson(); public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); public GsonHttpMessageConverter(){ super(new MediaType("application", "json", DEFAULT_CHARSET)); } @Override protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { try{ return gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz); }catch(JsonSyntaxException e){ throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e); } } @Override protected boolean supports(Class<?> clazz) { return true; } @Override protected void writeInternal(Object t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //TODO: adapt this to be able to receive a list of json objects too String json = gson.toJson(t); outputMessage.getBody().write(json.getBytes()); } //TODO: move this to a more appropriated utils class public String convertStreamToString(InputStream is) throws IOException { /* * To convert the InputStream to String we use the Reader.read(char[] * buffer) method. We iterate until the Reader return -1 which means * there''s no more data to read. We use the StringWriter class to * produce the string. */ if (is != null) { Writer writer = new StringWriter(); char[] buffer = new char[1024]; try { Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); int n; while ((n = reader.read(buffer)) != -1) { writer.write(buffer, 0, n); } } finally { is.close(); } return writer.toString(); } else { return ""; } }


Robby Pond es básicamente correcto, pero tenga en cuenta que su sugerencia de usar la etiqueta mvc: message-converters requiere que use 3.1. Dado que 3.1 actualmente es solo un lanzamiento de un hito (M1), le sugiero que registre su convertidor de esta manera después de crearlo:

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <util:list id="beanList"> <ref bean="someMessageConverter"/> <ref bean="someOtherMessageConverter"/> </util:list> </property> </bean>


Si desea agregar un convertidor de mensajes sin jugar con xml aquí hay un ejemplo simple

@Autowired private RequestMappingHandlerAdapter adapter; @PostConstruct public void initStuff() { List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters(); BufferedImageHttpMessageConverter imageConverter = new BufferedImageHttpMessageConverter();; messageConverters.add(0,imageConverter); }


Tuve una situación en la que el uso de Jackson requeriría que modificara el código de otro grupo (en la misma empresa). No me gustó eso. Así que elegí usar Gson y registrar TypeAdapters según sea necesario.

Enganchó un convertidor y escribió algunas pruebas de integración usando spring-test (que solía ser spring-mvc-test). No importa qué variación intenté (usando mvc: anotación o definición manual del bean). Ninguno de ellos funcionó. Cualquier combinación de estos siempre usó el convertidor Jackson que siguió fallando.

Respuesta > Resulta que el método de instalación autónoma de MockMvcBuilders codificó "duro" los convertidores de mensajes en versiones predeterminadas e ignoró todos mis cambios anteriores. Aquí está lo que funcionó:

@Autowired private RequestMappingHandlerAdapter adapter; public void someOperation() { StandaloneMockMvcBuilder smmb = MockMvcBuilders.standaloneSetup(controllerToTest); List<HttpMessageConverter<?>> converters = adapter.getMessageConverters(); HttpMessageConverter<?> ary[] = new HttpMessageConverter[converters.size()]; smmb.setMessageConverters(conveters.toArray(ary)); mockMvc = smmb.build(); . . }

Espero que esto ayude a alguien, al final utilicé el conversor de android impulsado por anotaciones y reorientación