【问题标题】:restTemplate.getForEntity when reading data that contains a list with single element gives Exception读取包含单个元素列表的数据时,restTemplate.getForEntity 会出现异常
【发布时间】:2019-03-13 02:14:41
【问题描述】:

我有一个 Detail 对象,它具有 Product 类型的对象属性。产品有一个名为 xxx 的属性,它是一个数组列表。我使用邮递员对 URL 进行 GET,结果如下所示:

"Product": {
    "id": "2",
    "xxx": [
        "price": "50"
      },
      {
        "price": "60"
      }
    ]
  }

这个结果很好。但是,在我的 Spring 项目中,当我将 RestTemplate 用作:

  restTemplate.getForEntity("someurl", Detail.class).getBody();

当 xxx 列表包含 2 个或更多元素时,我得到正确的结果。 但是,当此列表中只有元素时,出现错误:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of START_OBJECT token

  How do I fix this issue that I am facing with my call to restTemplate.getForEntity as above?

【问题讨论】:

  • 您的 JSON 不正确,看起来像是在发布前手动编辑的。
  • 你能提供未编辑的 + JSON 失败吗?

标签: java spring jackson resttemplate


【解决方案1】:

我怀疑您遇到此错误不是当您的列表包含一项时,而是当您根本没有列表而只有一个普通对象时,因此解析器抱怨错误放置的 START_OBJECT 令牌。 为了解决这个问题并且无法编辑 Swagger 生成的域类,您可以设置反序列化功能

ObjectMapper objectMapper = new ObjectMapper(); // maybe injected
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);

直接在您正在使用的ObjectMapper 上。

现在可以将此对象映射器放入RestTemplate 配置中:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class JsonControllerIT {
    @LocalServerPort
    private int port;

    @Test
    public void jsonWithSerializationFeatureSet() {
        // given
        RestTemplate restTemplate = new RestTemplate();
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        messageConverter.setObjectMapper(objectMapper);
        restTemplate.getMessageConverters().removeIf(m -> m.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));
        restTemplate.getMessageConverters().add(messageConverter);

        // when
        Detail detail = restTemplate.getForEntity("http://localhost:" + port + "/json", Detail.class).getBody();

        // then
        assertThat(detail.getSingleItemList().get(0)).isEqualTo(3);
    }
}

如果您使用 Spring 方法,请记住您也可以注入所有这些 bean,它们是在这里直接创建的,只是将它们一起更好地展示。

您也可以查看这个运行示例:

【讨论】:

  • 我正在调用restTemplate.getForEntity("someurl", Detail.class).getBody();我没有在任何地方使用 objectMapper。我在哪里可以访问此 ObjectMapper 以设置此值以及如何将此设置应用于 GET/post?也许我错过了一些概念。你能解释更多吗?
  • 我只是尝试了类似的东西(它不能解决我的问题): MappingJackson2HttpMessageConverter messageConverter = template.getMessageConverters().stream() .filter(MappingJackson2HttpMessageConverter.class::isInstance) .map(MappingJackson2HttpMessageConverter.class ::cast) .findFirst().orElseThrow( () -> new RuntimeException("MappingJackson2HttpMessageConverter not found")); ObjectMapper 映射器 = messageConverter.getObjectMapper(); mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
  • 我已经用一个示例更新了我的答案,如何将您配置的ObjectMapper 放入RestTemplate。玩得开心!
  • 我试过这个,我摆脱了那个错误。然而,它似乎还有另一个副作用。我也将此模板用于其他操作,例如发布、补丁等。所以随着这个变化,我开始收到另一个错误,补丁 - “HttpMessageNotReadableException:JSON解析错误:无法识别的字段“状态”(com.abc.mytest.model.Product类),未标记为可忽略;嵌套异常是com .fasterxml.jackson.databind.exc.UnrecognizedPropertyException:我不知道是什么导致了这个错误。有什么想法吗?
  • 如果您对反序列化配置有不同的要求,您必须使用不同的 RestTemplates 和另一个例如 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 来忽略未知字段。如果您想在所有反序列化过程中忽略未知字段,您也可以将此反序列化功能放在唯一的 RestTemplate 上。或者您可以在 Product 类上放置一个 @JsonIgnoreProperties(ignoreUnknown = true) 以忽略未知字段。
猜你喜欢
  • 2016-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多