Array Javax.ws.rs.processingexception: Error Reading Entity From Input Stream
[JAVA] After conducting a Bailiwick of jersey API test, I encountered a phenomenon where the JSR310 API could not exist deserialized.
Overview
When I created an API with the DTO class described afterwards equally the return value and performed an API test, the following exception occurred and the examination did not end normally.
javax.ws.rs.ProcessingException: Error reading entity from input stream. at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:889) at org.glassfish.jersey.bulletin.internal.InboundMessageContext.readEntity(InboundMessageContext.java:808) Caused past: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.LocalDateTime` (no Creators, like default construct, be): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 38](through reference chain: my.controller.MyDto["localDateTime"]) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452) environment
It was configured with Spring Boot.
- Jump Boot 2.ane.Ten
- Reproduce with Java 1.8-> Kotlin.
Details
Since Dto was written in Kotlin's data class, I thought it was an exception that a new case could not be created past the newInstance method because the constructor property is not allowed to be nada, but fifty-fifty if I rewrite information technology in java as shown beneath, it is the aforementioned. Exception occured.
MyDto.java
packet my.controller; import com.fasterxml.jackson.annotation.JsonAutoDetect; import java.time.LocalDateTime; @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) public class MyDto { public terminal String word; public concluding LocalDateTime localDateTime; /** *Argumentless constructor for Jackson to exercise a newInstance */ individual MyDto() { this(null, goose egg); } public MyDto(String word, LocalDateTime localDateTime) { this.word = word; this.localDateTime = localDateTime; } public String value() { return word + localDateTime.toString(); } } The resource class that takes this DTO as an argument and returns a value is as follows.
MyJerseyResource.java
parcel my.controller; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import java.fourth dimension.LocalDateTime; @Path("/my") public form MyJerseyResource { @Mail service @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public MyDto mail(MyDto myDto) { return new MyDto( myDto.value() + " finishes!", LocalDateTime.of(2019, 1, 1, 12, 0, 0) ); } @GET @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public MyDto get() { return new MyDto( "finish!", LocalDateTime.of(2019, ane, one, 12, 0, 0) ); } } Also, when it was started every bit a Spring Kicking application as usual, it was able to send and receive requests and responses normally when executed with Post Man.
For GET
For POST
Test class
I made a test class past setting this page. The get test ended with the above log, simply the post examination ended with a status of 400. The results will exist unlike, but the crusade of the test failure is the same equally described below.
MyJerseyResourceTest.java
package my.controller; import my.ConfigureTest; import my.jersey.MyJerseyTest; import org.junit.Earlier; import org.junit.Examination; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.kicking.exam.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.ws.rs.customer.Entity; import javax.ws.rs.cadre.MediaType; import javax.ws.rs.core.Response; import java.fourth dimension.LocalDateTime; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.cadre.Is.*; @RunWith(SpringRunner.class) @SpringBootTest(classes = {ConfigureTest.grade, MyJerseyTest.course}) public course MyJerseyResourceTest { @Autowired MyJerseyTest jerseyTest; @Before public void setUp() throws Exception { this.jerseyTest.setUp(); } @Test Examination public void mail service() { Response response = jerseyTest.webTarget("/my").request() .have(MediaType.APPLICATION_JSON_TYPE) .postal service( Entity.json(new MyDto( "start!", LocalDateTime.of(2018, one, 1, 12, one, ane) ) ) ); assertThat(response.getStatus(), is(200)); MyDto content = response.readEntity(MyDto.class); assertThat(content.word, is("start!2018-01-01T12:01:01 finishes!")); assertThat(content.localDateTime.toString(), is(LocalDateTime.of(2019, 1, one, 12, 0, 0).toString())); } @Test Test public void get() { Response response = jerseyTest.webTarget("/my").request() .accept(MediaType.APPLICATION_JSON_TYPE) .get(); assertThat(response.getStatus(), is(200)); MyDto content = response.readEntity(MyDto.course); assertThat(content.word, is("end!")); assertThat(content.localDateTime.toString(), is(LocalDateTime.of(2019, ane, 1, 12, 0, 0).toString())); } } solution
The cause seems to be that the ObjectMapper used by __Jersey does not support JSR310 __. Maybe Jersey's default behavior is to cull Jackson's ObjectMapper as the Json serializer / deserializer. The configuration itself is the same equally Leap MVC, simply unlike the exam of __Spring MVC, ObjectMapper does not support JSR310 __, and then it seems that such an error occurs. To resolve this consequence, I had to make some additional settings __ that apply simply when running __tests.
Gear up ObjectMapper that tin deserialize JSR310
The ObjectMapper registered in the container with Jump Boot supports JSR310, but JerseyTest doesn't seem to use it. JSR310 serializer, deserializer [JavaTimeModule](https://github.com/FasterXML/jackson-datatype-jsr310/blob/master/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule Register via .coffee). In the constructor of this form, the serializer and deserializer corresponding to the JSR310 class group are registered. One thing to note is the registration of the deserializer. I just couldn't solve the JSR310 deserialization problem described below. Therefore, information technology was necessary to prepare a custom deserializer. This time, I prepared only LocalDateTime, but I predict that if you lot use other APIs such as ZonedDateTime, you need to prepare a deserializer besides.
ConfigureTest.coffee
parcel my; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.note.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import java.fourth dimension.LocalDateTime; import java.time.format.DateTimeFormatter; @Configuration public class ConfigureTest { @Edible bean public ObjectMapper customObjectMapper(Environment environment) { JavaTimeModule m = new JavaTimeModule(); m.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); return Jackson2ObjectMapperBuilder.json() .modulesToInstall(grand) //If you do not disable information technology, it will exist formatted in Unix Time. .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .featuresToEnable(SerializationFeature.INDENT_OUTPUT) .build(); } } Get set to utilise the Beanized ObjectMapper
I was able to convert the ObjectMapper to a bean, merely equally it is, this beanized ObjectMapper volition not be used by Bailiwick of jersey. There seem to be several ways this ObjectMapper tin be used, but ContextResolver To make it available.
MyJacksonConfigurer.java
bundle my.bailiwick of jersey; import com.fasterxml.jackson.databind.ObjectMapper; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; @Provider @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class MyJacksonConfigurator implements ContextResolver<ObjectMapper> { private final ObjectMapper objectMapper; public MyJacksonConfigurator(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public ObjectMapper getContext(Class<?> type) { return this.objectMapper; } } I'1000 not sure because there is no explanation, only from the situation, if the type specified in the type parameter of the implementation destination ContextResolver is used in Jersey, that context will be obtained via the getContext method of this class. is. Since the bean-ized ObjectMapper is injected in the constructor of this class, you lot tin use the ObjectMapper corresponding to JSR310.
Procedure the request with a beanized ObjectMapper
I have defined a context, but this context is not registered with Jersey. If yous want to register some resources for Jersey'southward server-side processing, y'all need to annals with ResourceConfig and restore the settings with JerseyTest's configure method. By registering the above ContextResolver together with the Jersey resource class, the registered ObjectMapper tin can exist used when processing the request on the server side.
MyJerseyTest.java
@Override protected ResourceConfig configure() { return new ResourceConfig(MyJerseyResource.course) .annals(new MyJacksonConfigurator(objectMapper)) .property("contextConfig", applicationContext); } Process the response with the Beanized ObjectMapper
Jersey seems to make a strict distinction betwixt server-side and client-side settings. Server-side settings are the procedure of handling requests. The processing on the client side is the processing for handling the response. The deserializer on the server side could be registered, but the deserializer on the customer side could not be registered. This eliminates the 400 error when posting test code. Notwithstanding, when I endeavor to execute Response.readEntity, it seems that ObjectMapper that does not back up JSR310 is used, and the error quoted at the first occurs. To eliminate this fault, annals a ContextResolver with the client used by Jersey (in this example, the case that makes the request and receives the response when running the exam).
MyJerseyTest.coffee
@Override public Customer getClient() { render JerseyClientBuilder.createClient() .register(new MyJacksonConfigurator(objectMapper)); } You have at present registered an ObjectMapper for JSR310. The grade in which JerseyTest is set is as follows.
MyJerseyTest.java
package my.jersey; import com.fasterxml.jackson.databind.ObjectMapper; import my.controller.MyJerseyResource; import org.glassfish.jersey.client.JerseyClientBuilder; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; import org.glassfish.jersey.examination.JerseyTest; import org.glassfish.jersey.test.ServletDeploymentContext; import org.glassfish.jersey.exam.grizzly.GrizzlyWebTestContainerFactory; import org.glassfish.jersey.examination.spi.TestContainerFactory; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.springframework.web.filter.FormContentFilter; import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.filter.RequestContextFilter; import javax.ws.rs.customer.Client; import javax.ws.rs.customer.WebTarget; @Component public form MyJerseyTest { final JerseyTest jerseyTest; boolean start = false; public void setUp() throws Exception{ if (!beginning) { this.jerseyTest.setUp(); } showtime = true; } public WebTarget webTarget(String url) { render this.jerseyTest.target(url); } public MyJerseyTest(ApplicationContext applicationContext, ObjectMapper objectMapper) { this.jerseyTest = new JerseyTest() { @Override public Client getClient() { return JerseyClientBuilder.createClient() .register(new MyJacksonConfigurator(objectMapper)); } @Override protected ResourceConfig configure() { return new ResourceConfig(MyJerseyResource.class) .register(new MyJacksonConfigurator(objectMapper)) .property("contextConfig", applicationContext); } @Override protected ServletDeploymentContext configureDeployment() { return ServletDeploymentContext .forServlet(new ServletContainer(configure())) .addFilter(HiddenHttpMethodFilter.class, HiddenHttpMethodFilter.course.getSimpleName()) .addFilter(FormContentFilter.class, FormContentFilter.class.getSimpleName()) .addFilter(RequestContextFilter.class, RequestContextFilter.form.getSimpleName()) .build(); } @Override public TestContainerFactory getTestContainerFactory() { return new GrizzlyWebTestContainerFactory(); } }; } } Create a Deserializer for LocalDateTime
Equally mentioned above, I registered a LocalDateTime deserializer with ObjectMapper, just this had to be customized. This is considering the default deserializer cannot deserialize the strings generated by Jersey. It can be processed with the specified deserializer There may exist a way, but I just couldn't find an alternative. Nosotros accept adopted the following for this solution.
CustomLocalDateDeserializer.coffee
package my; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonTokenId; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import java.io.IOException; import java.fourth dimension.LocalDateTime; import java.time.format.DateTimeFormatter; public class CustomLocalDateTimeDeserializer extends LocalDateTimeDeserializer { public CustomLocalDateTimeDeserializer(DateTimeFormatter pattern) { super(pattern); } @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { if ( p.hasTokenId(JsonTokenId.ID_STRING) || p.isExpectedStartArrayToken()) { return super.deserialize(p, ctxt); } else { ObjectNode node = p.getCodec().readTree(p); render LocalDateTime.of( node.become("yr").asInt(), node.become("monthValue").asInt(), node.go("dayOfMonth").asInt(), node.get("hr").asInt(), node.become("minute").asInt(), node.get("second").asInt() ); } } } The reason for this is that when the Response.readEntity method is executed in the test grade, the cord of the LocalDateTime value that tin can exist obtained by the default Jackson LocalDateTime deserializer that is executed in the innermost part is It's non the format Jackson expects. At the timing when the response object was created, the format of the LocalDateTime string was inverse to the following format no matter what.
.json
{ "localDateTime": { "dayOfYear": 1, "dayOfWeek": "WEDNESDAY", "month": "Jan", "dayOfMonth": 1, "yr": 2018, "monthValue": 1, "60 minutes": 1, "minute": 0, "second": 0, "nano": 0, "chronology": { "id": "ISO", "calendarType": "iso8601" } } } What practise y'all mean? Each member object of LocalDateTime has been carefully serialized into JSON format. Instead of doing this, I should at least obediently do LocalDateTime.toString (). You'll have to deserialize yourself to do this extra thing. When deserializing, Jackson stores the character string data to exist deserialized every bit a character string in JsonParser, the first statement of the deserialize method, only when deserializing to LocalDateTime, this format character string is stored in LocalDateTime. Does non have the ability to deserialize to. The starting time branch in the above code,
- p.hasTokenId (JsonTokenId.ID_STRING)-> 2019-01-01T12: 00: 00 format
- p.isExpectedStartArrayToken ()-> [2019, 1, one, 12, 0, 0] format
If a format other than either of the above comes, an exception to the quote at the offset will occur. In this instance, the string plain starts with "{", so the character to be deserialized is considered an object and cannot be deserialized. [^ ane] If yous get a format that Jackson doesn't support, yous'll have to deserialize information technology yourself, so I had no pick but to use this implementation. I was wondering why it came in this format in the first place, so I debugged the Jersey code, just decided that it was besides complicated and wasted fourth dimension to decipher information technology, so I didn't go deeper. .. ..
Impressions
Bound Boot is doing a lot of piece of work, but it seems that some parts of Jersey are out of reach. With Jump MVC, yous can even test seamlessly with Spring Boot, then this problem does not occur. You tin can examination without thinking most annihilation. And JAX-RS seems to force y'all to empathise the details of the specification before using information technology. The time available for the states is limited. If y'all spend your time remembering something that can't exist done, you want to spend your time doing other things. Originally, the framework should do its best to reduce such time, but it is besides a skillful indicate to fall over. After all, when I chose Bound Kicking, I realized that there was no merit in choosing Jersey (JAX-RS). ~~ In the start place, the benefits of building a web application other than Jump Boot in Coffee are not clear at this stage, so it seems that there is no need to utilise JAX-RS. ~~
Supplement
I found two methods other than using the Beanized ObjectMapper with ContextResolver, and then I volition write them downward. In both cases, it is necessary to register the instance on the server side and the client side.
Employ JacksonJaxbJsonProvider
Past default, ObjectMapper is used for serializing and deserializing Json in Jersey, but information technology seems that this behavior is achieved by using a course called JacksonJaxbJsonProvider. Therefore, override this class.
CustomJacksonJsonProvider.java
package my; import com.fasterxml.jackson.databind.ObjectMapper; import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider; import javax.annotation.Priority; import javax.ws.rs.ext.Provider; @Provider @Priority(0) public class CustomJacksonJsonProvider extends JacksonJaxbJsonProvider { public CustomJacksonJsonProvider(ObjectMapper objectMapper) { super(); super.setMapper(objectMapper); } } Implement MessageBodyReader and MessageBodyWrite
The body content that comes into the Bailiwick of jersey response seems to go into the implementation of the championship form. Information technology seems to read and write the body by solving this grade, and y'all can hook serialization and deserialization here. Nevertheless, this method is non recommended at all considering information technology requires extra implementation, is complicated, and it is unclear whether information technology is really practical in the first place. For reference only.
MyMessageBody.java
packet my; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.cadre.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; import coffee.lang.note.Annotation; import java.lang.reflect.Blazon; @Provider @Produces(value ={MediaType.APPLICATION_JSON}) @Consumes(value ={MediaType.APPLICATION_JSON}) public class MyMessageBody<T extends Serializable> implements MessageBodyReader<T>, MessageBodyWriter<T> { @Override public boolean isReadable(Class<?> type, Type genericType, Note[] annotations, MediaType mediaType) { return true; } @Override public T readFrom(Grade<T> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Cord> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { //Content to serialize return aught; } @Override public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return true; } @Override public void writeTo(T t, Class<?> blazon, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<Cord, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { //Content to deserialize } } [^ 1]: As an aside, JsonParser has the information to deserialize itself every bit a string. If the string that JsonParse has does not start with "{", "[", it is a string (ID_STRING), if it starts with "{", it is an object (START_OBJECT), if it starts with "[", it is an array ( It seems to be judged as START_ARRAY).
macdonaldmurst1966.blogspot.com
Source: https://linuxtut.com/en/7f7ca9bdbd1d3e3c4851/
0 Response to "Array Javax.ws.rs.processingexception: Error Reading Entity From Input Stream"
Postar um comentário