Saturday, September 16, 2023

No String Argument Constructor/Factory Method to Deserialize From String Value

 In this short article, we will cover in-depth the JsonMappingException: no String-argument constructor/factory method to deserialize from String value () exception.

First, we will explain the root cause behind this exception. Then, we will showcase how to reproduce and solve it using practical examples.

The Cause

Before diving deep into the exception, let’s understand what its stack trace means.

Typically, “no String-argument constructor/factory method to deserialize from String” tells us that Jackson fails to find a suitable constructor or factory method to deserialize a JSON string into an object.

In short, the exception occurs when something goes wrong while deserializing a JSON string.

The leading cause behind it is trying to perform deserialization using double quotes ”…” instead of bracelets {…}.

Jackson interprets the double quotes as a string. So it expects the target object to have a string argument constructor or a factory method. Hence the error no String-argument constructor/factory method.

Another reason would be accidentally using the convertValue() method instead of readValue().

Reproducing no String-argument constructor/factory method to deserialize from String

Now that we know what the stack trace means, let’s see how to reproduce it in practice.

For instance, let’s consider the Person class:

    
        public class Person {

            private String firstName;
            private String lastName;
            private Email email;

            public String getFirstName() {
                return firstName;
            }
            public void setFirstName(String firstName) {
                this.firstName = firstName;
            }
            public String getLastName() {
                return lastName;
            }
            public Email getEmail() {
                return email;
            }
            public void setEmail(Email email) {
                this.email = email;
            }
        }
    

Next, we are going to create the Email class:

    
        public class Email {
            private String account;
            public String getAccount() {
                return account;
            }
            public void setAccount(String account) {
                this.account = account;
            }
        }
    

Now, let’s add a static method to deserialize a JSON string into a Person object:

    
        public static void deserializeJsonString() {
            ObjectMapper mapper = new ObjectMapper();
            String personJson = "{" + 
                                    "\"firstName\": \"Azhrioun\"," + 
                                    "\"lastName\": \"Abderrahim\"," + 
                                    "\"email\": \"gaccount@gmail.com\""  +  
                                "}";

            try {
                Person person = mapper.readValue(personJson, Person.class);
                System.out.println("Email Account: " + person.getEmail().getAccount());
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
    

As we can see, we used a string value to denote the JSON property email.

Finally, let’s execute the method and take a close look at the stack trace:

    
        com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of ...
        no String-argument constructor/factory method to deserialize from String value ('gaccount@gmail.com')
    

As shown above, Jackson fails with the exception because it could not convert the specified JSON value gaccount@gmail.com, which is a string, into an object of the Email type.

This behavior makes sense. In order to perform deserialization, Jackson needs to call a constructor which takes String email as an argument.

Fixing the Exception

The easiest solution would be defining a string argument constructor in our class Person.

This constructor will be used by Jackson later to handle the JSON deserialization.

Here’s an example:

    
        public Person(@JsonProperty("firstName") String firstName, 
                      @JsonProperty("lastName") String lastName, 
                      @JsonProperty("email") String email) {
            this.firstName = firstName;
            this.lastName = lastName;
        
            Email emailObj = new Email();
            emailObj.setAccount(email);
            this.email = emailObj;
        }
    

We used the @JsonProperty annotation to denote the property names in the given JSON.

As we can see, we created a new Email object. Then, we used the passed string argument to set the account field.

Alternatively, we can annotate a custom method that accepts a string parameter with @JsonProperty(“email”).

So, let’s see it in action:

    
        @JsonProperty("email")
        public void deserializeEmail(String email) {
            Email emailObj = new Email();
            emailObj.setAccount(email);
            this.email = emailObj;
        }
    

That way, we tell Jackson to call deserializeEmail() when deserializing the JSON property “email”: “gaccount@gmail.com”.

Another approach would be using a static factory method instead of the string argument constructor:

    
        @JsonCreator
        public static Person of(@JsonProperty("firstName") String firstName, 
                                @JsonProperty("lastName") String lastName, 
                                @JsonProperty("email") String email) {
            Person person = new Person();
            person.firstName = firstName;
            person.lastName = lastName;

            Email emailObj = new Email();
            emailObj.setAccount(email);
            person.email = emailObj;

            return person;
        }
    

Simply put, @JsonCreator marks the factory method defining how to deserialize the JSON. It’s a good practice when the specified JSON doesn’t match the target class.

Synchronous and asynchronous REST clients

 REST clients can be implemented either synchronously or asynchronously. Both MicroProfile Rest Client and JAX-RS can enable asynchronous clients.

A synchronous client constructs an HTTP structure, sends a request, and waits for a response. An asynchronous client constructs an HTTP structure, sends a request, and moves on. In this case, the client is notified when the response arrives. The original thread, or another thread, can then process the response. Although asynchronous behavior can result in faster overall execution, synchronous behavior might be preferred in certain cases where more simplified code is necessary.

Synchronous REST clients

Synchronous REST clients take advantage of the inherent simplicity of REST architecture by using concise, streamlined code to send clearly defined HTTP requests. If a client is implemented synchronously, you don’t need to worry about managing callbacks, Future, or CompletionStage return types. Synchronous clients might be preferred in situations where service availability is high and low latency is a priority.

However, a synchronous client must wait for an API call to return before code execution can continue. In some cases, this delay might be perceived by users of your app as latency or poor performance. If an application consists of microservices that are making synchronous calls to one another, one failure might set off a chain reaction that results in service denial for the user.

Asynchronous REST clients

Implementing asynchronous REST clients can be a powerful strategy for microservices-based applications. Asynchronous clients don’t need to wait for a response to continue working. Therefore, microservices in an app can continue to process and send data, even when one of their partner services runs into trouble. This capability provides more reliable service to the user and can be especially valuable in cases where service availability is low or overloaded with demand.

For example, asynchronous clients might be implemented within a travel service app that makes REST calls to an airline service, hotel service, and car rental service. If the hotel service experiences a lag, the airline and car rental service are able to continue working. For an in-depth look at how to implement asynchronous clients in this travel app example, check out Andy McCright’s blog post on Asynchronous REST with JAX-RS and MicroProfile.

Asynchronous REST clients with MicroProfile Rest Client

Callbacks are functions that run after another function completes. With MicroProfile Rest Client, asynchronous clients rely on callbacks to manage the transfer of data between microservices. The return type of the interface method determines whether a RESTful service is invoked synchronously or asynchronously. If the return type is a CompletionStage, the service is invoked asynchronously. A CompletionStage return type acts as a promise to the service that a particular piece of code must be executed at some point in the future. A wide range of callbacks can be attached to a CompletionStage return type, which can enable different functions after the code executes. Therefore, non-blocking systems can be implemented among microservices in an application.

MicroProfile Rest Client is enabled to set up both synchronous and asynchronous REST clients. You can use MicroProfile Rest Client with Open Liberty by enabling the mpRestClient feature in your server.xml file.

The following example shows a MicroProfile Rest Client interface that has methods to invoke the same remote service both synchronously and asynchronously:

@Path("/somePath")
public interface MyClient {

    @GET
    Widget getSync();

    @GET
    CompletionStage<Widget> getAsync();
}


The following example shows the code to invoke that remote service:

MyClient client = RestClientBuilder.newBuilder().baseUri(someUri).build(MyClient.class);

CompletionStage<Widget> cs = client.getAsync();
// while that request is running, let's invoke it synchronously too:

Widget widgetSync = client.getSync(); // now we have to wait until the request completes
// done.  So now we can see the results of the async request
// that was processing while we did the sync request:

Widget widgetAsync = toCompletableFuture().get();

assertEquals(widgetAsync, widgetSync);


Asynchronous REST clients with JAX-RS

With JAX-RS clients, you need to explicitly build an async client with an API call, as shown in the following example:

Client client = ClientBuilder.newClient();
WebTarget target = client.target(url);
Invoker.Builder builder = target.request();
AsyncInvoker asyncInvoker = builder.async();


Then, you can choose whether to provide a callback or get a Future return type, as shown in the following example.

Future<MyResponseObject> future = asyncInvoker.get(MyResponseObject.class);
// or
Future<MyResponseObject> future = asyncInvoker.get( new InvocationCallback<MyResponseObject>() {
    @Override
    public void completed(MyResponseObject o) {
        // do something with o
    }

    @Override
    public void failed(Throwable t) {
        // handle the failed request/response
    }
});


Synchronous and asynchronous REST with MicroProfile

Ready to start building microservices with synchronous and asynchronous REST clients? MicroProfile Rest Client provides a type-safe approach to invoke RESTful services. Although the default implementation is synchronous, you can also make asynchronous calls by using the CompletionStage interface. MicroProfile Rest Client provides an easy-to-build template that gets you up and running with RESTful microservices faster, and without having to worry about the boilerplate code. You can also use the MicroProfile Fault Tolerance feature to make your asynchronous clients more reliable. MicroProfile Fault Tolerance includes the @Asynchronous annotation, which enables any method within a class to be invoked by a separate thread. You can use this function with Open Liberty by enabling the mpFaultTolerance feature in your server.xml file.

No String Argument Constructor/Factory Method to Deserialize From String Value

  In this short article, we will cover in-depth the   JsonMappingException: no String-argument constructor/factory method to deserialize fro...