JsonFormat$Value.hasLenient() 在进行Rest调用时出错

huangapple go评论106阅读模式
英文:

JsonFormat$Value.hasLenient() Error on Rest Call

问题

我正在使用Spring Boot构建一组微服务。我有一个服务与数据库进行交互,并具有一系列RESTful端点,其他应用程序可以访问。这些表实体中的许多都是复杂对象,并以JSON对象的形式提供,但有些是简单的参数,因此以字符串形式提供。当我访问主数据库服务端点时,所有数据都能够正常返回,没有问题。

我有一系列其他应用程序以不同的方式消耗这些数据。因为许多对象在应用程序之间被重用(数据库服务响应形状,人员实体POJO等),所以我有一个包含一些常见类的包。每个应用程序都导入这些常见类,然后通过RestTemplate调用主数据库服务。因为响应始终以标准方式包装,所以我通过执行一个期望带有适当POJO或Java变量(String、int等)的响应包装器的ParameterizedTypeReference来获取值。

截止到5个月前,一切都很好。该应用程序部署到服务器上,仍然正常运行。然而,最近我回到代码中进行一些修改,现在我发现当我在本地运行应用程序时,一些端点会出现以下错误:

java.lang.NoSuchMethodError: com.fasterxml.jackson.annotation.JsonFormat$Value.hasLenient()Z

起初我以为可能两个POM文件不同步,其中一个引入了Jackson的更新版本,但在POM中硬编码版本以确保它们完全相同(版本为2.10.1)后,仍然存在相同的问题。还应该注意到,错误消息中的方法似乎是在2.9版本中添加的,截止到目前为止尚未被弃用。这个错误只在尝试从返回POJO的端点检索数据时发生,而当数据类型为String时不会发生。也可能与这个调用似乎在抛出错误之前从未实际离开应用程序有关,因为数据库服务端点从未被访问。

你有什么想法为什么会出现这个错误,以及如何解决它?

响应对象:

public class DatabaseServiceResponse<T> {
    @JsonInclude(Include.NON_NULL)
    private List<T> entities;
    
    @JsonInclude(Include.NON_NULL)
    private String error;
    
    // 省略了getter和setter
}

人员服务:

public List<Person> getPeople() {
    List<Person> people = new ArrayList<>();
    HttpHeaders headers = getHeaders();
    HttpEntity<Object> requestEntity = new HttpEntity<>(null, headers);
    ResponseEntity<DatabaseServiceResponse<Person>> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, new ParameterizedTypeReference<DatabaseServiceResponse<Person>>() {});  // 在这一行出错
    if (response.getStatusCode() == HttpStatus.OK) {
        people = response.getBody().getEntities();
    } else {
        throw new ServiceException("Error retrieving people");
    }
    return people;
}

public List<String> getPersonTypes() {
    List<String> personTypes = new ArrayList<>();
    HttpHeaders headers = getHeaders();
    HttpEntity<Object> requestEntity = new HttpEntity<>(null, headers);
    ResponseEntity<DatabaseServiceResponse<String>> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, new ParameterizedTypeReference<DatabaseServiceResponse<String>>() {});  // 在这里正常工作
    if (response.getStatusCode() == HttpStatus.OK) {
        personTypes = response.getBody().getEntities();
    } else {
        throw a ServiceException("Error retrieving person types");
    }
    return personTypes;
}

人员应用程序的POM:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath />
</parent>

常见元素的POM:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.1</version>
    <scope>provided</scope>
</dependency>

更新
我认为外部库和可能不同步的POM文件是一个误导。我已将所有相关类复制到主项目中,以消除对外部包的依赖,并且仍然存在相同的问题。我还尝试完全升级所有版本到最新版本,但仍然没有运气。不知何故,Jackson只会将JSON映射到String或String列表,而不会将我的JSON映射到POJO。

英文:

I'm building a set of microservices in Spring Boot. I've got one service which interfaces with the database and has a series of RESTful endpoints which other apps can hit. Many of these table entities are complex objects and are served up as a JSON object but some are simple parameters and so are served as strings. When I hit the main DB service endpoints, all data is returned fine without issue.

I have a series of other applications which consume this data in various ways. Because many objects are reused across applications (the DB service response shape, the person entity POJO, etc), I've got a package made with some common classes. Each app imports these common classes and then makes calls to the main DB service via RestTemplate. Because the response is always wrapped in a standard way, I am getting the values by performing an exchange which expects a ParameterizedTypeReference of the response wrapper with the appropriate POJO or Java variable (String, int, etc).

As of 5 months ago, all was good. The app was deployed to a server and is still operating normally. However, I recently went back in to the code in order to make some modifications and now I find that when I run the app locally some of the endpoints fail with the following error:

java.lang.NoSuchMethodError: com.fasterxml.jackson.annotation.JsonFormat$Value.hasLenient()Z

I thought at first that perhaps the two POMs were out of sync and one was pulling a newer version of Jackson but after hard coding the version in the POM to be 100% certain they were the same (version 2.10.1), it still has the same issue. It should also be noted that the method in the error message appears to have been added in version 2.9 and has not been deprecated as of this moment in time. This error only happens when trying to retrieve data from endpoints returning POJOs and not when the data type is a String. It may also be relevant that the call appears to never actually leave the app before throwing an error as the DB service endpoint never gets hit.

Any ideas why I'm getting this error and how I could resolve it?

Response Object:

public class DatabaseServiceResponse&lt;T&gt; {
	@JsonInclude(Include.NON_NULL)
	private List&lt;T&gt; entities;
	
	@JsonInclude(Include.NON_NULL)
	private String error;
	
	[getters and setters ommitted]
}

Person Service:

public List&lt;Person&gt; getPeople() {
	List&lt;Person&gt; people = new ArrayList&lt;&gt;();
	HttpHeaders headers = getHeaders();
	HttpEntity&lt;Object&gt; requestEntity = new HttpEntity&lt;&gt;(null, headers);
	ResponseEntity&lt;DatabaseServiceResponse&lt;Person&gt;&gt; response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, new ParamaterizedTypeReference&lt;DatabaseServiceResponse&lt;Person&gt;&gt;() {});  // Fails on this line
	if (response.getStatusCode() == HttpStatus.OK) {
		people = response.getBody().getEntities();
	} else {
		throw new ServiceException(&quot;Error retrieving people&quot;);
	}
	return people;
}

public List&lt;String&gt; getPersonTypes() {
	List&lt;String&gt; personTypes = new ArrayList&lt;&gt;();
	HttpHeaders headers = getHeaders();
	HttpEntity&lt;Object&gt; requestEntity = new HttpEntity&lt;&gt;(null, headers);
	ResponseEntity&lt;DatabaseServiceResponse&lt;String&gt;&gt; response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, new ParamaterizedTypeReference&lt;DatabaseServiceResponse&lt;String&gt;&gt;() {});  // Works fine here
	if (response.getStatusCode() == HttpStatus.OK) {
		personTypes = response.getBody().getEntities();
	} else {
		throw new ServiceException(&quot;Error retrieving person types&quot;);
	}
	return personTypes;
}

Person App POM:

&lt;parent&gt;
	&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
	&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
	&lt;version&gt;2.2.2.RELEASE&lt;/version&gt;
	&lt;relativePath /&gt;
&lt;/parent&gt;

Common Elements POM:

&lt;dependency&gt;
	&lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
	&lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;
	&lt;version&gt;2.10.1&lt;/version&gt;
	&lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;

UPDATE
I believe the external library and the potentially out-of-sync POM files was a red herring. I have copied all of the relevant classes into the main project to remove the dependency on the external package and I have the same issue. I've also tried completely upgrading all versions to the latest and still no luck. Jackson, for whatever reason, will map JSON to a String or a list of Strings but just will not map my JSON to a POJO.

答案1

得分: 1

终于弄清楚了这里的问题,感觉如此荒谬,我不相信任何人没有代码在手中的情况下会找到正确答案。只有通过将所有代码汇总到一个地方,然后试图找到另一种通过Jackson映射对象的方法(直接使用ObjectMapper而不是让RestTemplate来处理)我偶然发现了真正的问题。

有一个审计类,它是我的Person对象的属性,其中包含了由创建者、创建日期、修改者和修改日期组成的常规基本属性。这些日期是标准ISO-8601格式的LocalDateTime对象。事实证明,Jackson不再能够直接将字符串处理为LocalDateTime对象,需要“帮助”。通过添加一个接受日期字符串并执行LocalDateTime.parse(date)来设置实际属性的setter,我现在能够成功地随意访问端点。

我仍然不知道为什么会抛出上面的精确错误,但就是这样。通过没有指定从StringLocalDateTime对象的翻译方式,我收到了来自Jackson的JsonFormat$Value.hasLenient()错误。

英文:

I finally worked out what was wrong here and it feels so absurd that I don't believe anyone would ever have gotten to the right answer without the code in their hands. It was only by pulling all the code back into one place and then seeking to find another means of mapping the object via Jackson (using an ObjectMapper directly as opposed to letting the RestTemplate figure that out) that I stumbled upon the true problem.

There is an audit class which is a property on my Person object which contains the normal, basic properties of created by, created date, modified by, modified date. These dates are LocalDateTime objects in the standard ISO-8601 format. It turns out that Jackson can no longer process the string straight into the LocalDateTime object and needs "help". By adding a setter which takes the date as a string and does a LocalDateTime.parse(date) to set the actual property, I'm now able to successfully hit the endpoints at will.

I still have no clue why the exact error above was thrown, but there you have it. By not specifying a way to help translate from a String to the LocalDateTime object, I received the JsonFormat$Value.hasLenient() error from Jackson.

答案2

得分: 1

我遇到了类似的问题。

在我的情况下,@Eyowzitgoin的回答帮助我解决了我的问题。我在jackson注解上遇到了Maven依赖冲突。

我尝试运行一些测试,在命令行上一切正常,但是IntelliJ在选择版本时出现了问题。我明确地声明了2.10.3,但通过传递依赖项选择了2.8.5

2.10.3有一个方法JsonFormat$Value.hasLenient,但2.8.5没有。

我在一个POJO的字符串变量上使用了JsonFormat注解,IntelliJ期望能够调用这个方法,但却无法这样做。

在我的情况下,我没有解决问题的根本原因,因为变量本应该是一个LocalDate,所以我改变了类型并删除了注解,问题就解决了。

我期望如果我保留类型不变,保留注解,并解决依赖问题,确保只有一个版本存在/被声明,问题也会得到解决。

英文:

I had a similar issue.

In my case @Eyowzitgoin's answer helped me figure out my issue. I had a maven dependency conflict on the jackson annotations.

I was trying to run some tests, and all was fine on the command line but intellij had an issue knowing which version to pick. I had 2.10.3 explicitly declared, but 2.8.5 was being picked up through a transitive dependency.

2.10.3 has a method JsonFormat$Value.hasLenient, but 2.8.5 doesn't.

I was using the JsonFormat annotation on a String variable in a pojo, and intellij was expecting to be able to call the method, but was unable to do so.

In my case I didn't resolve the root of the problem because the variable should have been a LocalDate, so I changed the type and removed the annotation and the issue went away.

I would expect that if I'd left the types as they were and the annotation in place, and instead resolved the dependency issue and made sure that only one of the versions was present/declared the issue would also have been resolved.

huangapple
  • 本文由 发表于 2020年9月5日 02:49:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/63746595.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定