Spring RestTemplate失败,带有大写元素名称

时间:2016-04-18 14:27:56

标签: spring-mvc jaxb resttemplate

我有一个简单的Spring MVC应用程序,它使用RestTemplate类对ReST服务进行Ajax调用。模式中的一些元素以大写字母开头。带有小写字母的2个元素(代码,消息)不会产生任何问题。 JAXB生成的类具有@XmlElement注释和name属性。这似乎被忽略了。我已经读过需要使用JaxbAnnotationIntrospector但没有任何更改生效。见下面的课程。我已经尝试在Spring配置中为RestTemplate添加一个bean类定义,添加一个对象映射器,但没有任何帮助。在堆栈跟踪的前几行中查看OTPRO元素的错误:

[4/18/16 9:52:43:988 EDT] 00000024 SystemErr     R org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unrecognized field "OTPRO" (class com.ssdr.rest.message.SSDRResponse), not marked as ignorable (7 known properties: "dt", "ot", "message", "otpro", "otphone", "code", "dtphone"])
 at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@4ccbe679; line: 4, column: 14] (through reference chain: com.ssdr.rest.message.SSDRResponse["OTPRO"]); nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "OTPRO" (class com.ssdr.rest.message.SSDRResponse), not marked as ignorable (7 known properties: "dt", "ot", "message", "otpro", "otphone", "code", "dtphone"])
 at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@4ccbe679; line: 4, column: 14] (through reference chain: com.ssdr.rest.message.SSDRResponse["OTPRO"])
[4/18/16 9:52:44:019 EDT] 00000024 SystemErr     R  at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readInternal(MappingJackson2HttpMessageConverter.java:126)
[4/18/16 9:52:44:019 EDT] 00000024 SystemErr     R  at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:153)
[4/18/16 9:52:44:019 EDT] 00000024 SystemErr     R  at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:81)

JAXB生成的类:

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;

/**
 * <p>Java class for documentResponse complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="documentResponse">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="code" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         &lt;element name="message" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         &lt;element name="OTPRO" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="OT" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="OTPhone" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="DT" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="DTPhone" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "documentResponse", propOrder = {
"code",
"message",
"otpro",
"ot",
"otPhone",
"dt",
"dtPhone"
})
public class DocumentResponse {

@XmlElement(required = true)
protected String code;
@XmlElement(required = true)
protected String message;
@XmlElement(name = "OTPRO", nillable = true)
protected String otpro;
@XmlElement(name = "OT", nillable = true)
protected String ot;
@XmlElement(name = "OTPhone", nillable = true)
protected String otPhone;
@XmlElement(name = "DT", nillable = true)
protected String dt;
@XmlElement(name = "DTPhone", nillable = true)
protected String dtPhone;
...

服务类:

    SSDRResponse resp = null;
    RestTemplate restTemplate = new RestTemplate();
    HttpEntity<SSDRRequest> httpRequest = new HttpEntity<SSDRRequest>(req, createHeaders());
    resp = restTemplate.postForObject(SERVICE_URI, httpRequest, SSDRResponse.class);

Spring config:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <context:annotation-config />

    <!-- Scans the classpath of this application for @Components to deploy as beans -->
    <context:component-scan base-package="com.ssdr" />

    <!-- Configures the @Controller programming model -->
    <mvc:annotation-driven />

    <!-- Forwards requests to the "/" resource to the "index" view -->
 <!--  
    <mvc:view-controller path="/" view-name="index" />
-->
    <!-- Make webapp/resources directory accessible to web app -->
    <mvc:resources location="/resources/" mapping="/resources/**" />

    <!-- Resolves view names to protected .jsp resources within the context root directory -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean> 

<bean class="org.springframework.web.client.RestTemplate">
    <property name="messageConverters">
        <list>
            <bean
                class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="supportedMediaTypes" value="application/json" />
                <property name="objectMapper">
                    <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                        <property name="annotationIntrospector">
                            <bean
                                class="com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector" />
                        </property>
                    </bean>
                </property>
            </bean>
        </list>
    </property>
</bean>

    <!-- <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
      <property name="mediaTypes">
        <map>
          <entry key="html" value="text/html"/>
          <entry key="json" value="application/json"/>
        </map>
      </property>
      <property name="viewResolvers">
        <list>
          <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
          </bean>
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
          </bean>
        </list>
      </property>
      <property name="defaultViews">
        <list>
          <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
            <property name="prefixJson" value="true"/>
          </bean>
        </list>
      </property>
    </bean> -->

</beans>

注意:我不想简单地忽略大写元素。

2 个答案:

答案 0 :(得分:1)

解决方案是使用JacksonAnnotationIntrospector和JaxbAnnotationIntrospector的组合。在这种情况下,Jackson需要识别请求中的@JsonIgnoreProperties和@JsonInclude注释。但杰克逊总是抛出“无法识别的领域”错误。 JAXB可以使用大写字段读取响应,但不会识别请求中的注释。

要使用两个内省检索器,以下代码已添加到服务类中:

    List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
    MappingJackson2HttpMessageConverter jaxMsgConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper objMapper = new ObjectMapper();
    /*
     * Jackson introspector needed for @JsonIgnoreProperties and @JsonInclude annotations
     * JAXB introspector is needed to handle the uppercase element names in the response
     */
    AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
    AnnotationIntrospector secondary = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
    AnnotationIntrospector pair = AnnotationIntrospector.pair(primary, secondary);
    objMapper.setAnnotationIntrospector(pair);
    jaxMsgConverter.setObjectMapper(objMapper);
    messageConverters.add(jaxMsgConverter);

    SSDRResponse resp = null;
    RestTemplate restTemplate = new RestTemplate();
    // Set message converter with Jackson and JAXB introspectors in RestTemplate
    restTemplate.setMessageConverters(messageConverters);
    HttpEntity<SSDRRequest> httpRequest = new HttpEntity<SSDRRequest>(req, createHeaders());
    resp = restTemplate.postForObject(SERVICE_URI, httpRequest, SSDRResponse.class);

仅使用JAXB introspector:

    List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
    MappingJackson2HttpMessageConverter jaxMsgConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper jaxMapper = new ObjectMapper();
    AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
    jaxMapper.setAnnotationIntrospector(introspector);
    jaxMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // ignore unknown properties
    jaxMsgConverter.setObjectMapper(jaxMapper);
    messageConverters.add(jaxMsgConverter);

答案 1 :(得分:0)

我的春季启动配置(准备使用):

@Configuration
@EnableWebMvc
@EnableAutoConfiguration
@ComponentScan(basePackages = {"path.to.your.package"})
public class WebMvcConfig extends WebMvcConfigurerAdapter {
   @Bean
   public ObjectMapper objectMapper() {
      ObjectMapper jacksonMapper = new ObjectMapper();
      AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
      AnnotationIntrospector secondary = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance());
      AnnotationIntrospector pair = AnnotationIntrospector.pair(primary, secondary);

      jacksonMapper.setAnnotationIntrospector(pair);
      jacksonMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
      return jacksonMapper;
   }

   @Bean
   public RestTemplate getRestTemplate() {
      List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
      MappingJackson2HttpMessageConverter jaxMsgConverter = new MappingJackson2HttpMessageConverter();

      ObjectMapper objectMapper = objectMapper();
      jaxMsgConverter.setObjectMapper(objectMapper);
      messageConverters.add(jaxMsgConverter);

      HttpClientBuilder builder = HttpClientBuilder.create();
      HttpClient httpClient = builder.build();
      HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
      RestTemplate template = new RestTemplate(new BufferingClientHttpRequestFactory(factory));

      template.setMessageConverters(messageConverters);

      return template;
   }
}