0

Having this example JAX-RS web-service, the second parameter of the getByAttribute is of the type Object.class.

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/POJOService")
public interface POJOService extends BasicService {

    @GET
    @Path("/getByAttribute")
    SampleObject getByAttribute(@QueryParam("attribute") String attribute, @QueryParam("value") Object value);

}

This will result in an error:

Payload: Parameter Class java.lang.Object has no constructor with single String parameter, static valueOf(String) or fromString(String) methods

I was thinking of adding a provider of javax.ws.rs.ext.ParamConverterProvider which will pass the type in the JSON string. But maybe there is a best practice solutions for this problem?

Migrating the communication layer of an existing enterprise server/client application to JAX-RS 2.1.
Using Apache CXF and Eclipse Rest Client for MicroProfile.

EDIT: The client will call the service with different types:

service.getByAttribute("name", "example"); // string, used for this test which throws the exception
service.getByAttribute("id", 99); // integer
service.getByAttribute("author", user); // object instace of User.class
flavio.donze
  • 7,432
  • 9
  • 58
  • 91
  • What does the URL look like, what is the value of the `value` query parameter? Is it just a string? Then why don't you use `@QueryParam("value") String value`? –  Jul 12 '19 at 13:21
  • The Eclipse REST Client does not recommend to use `Object` as the type of a parameter. Use a primitive type or a class that can be mapped to/from JSON or XML. –  Jul 12 '19 at 16:32
  • We are migrating from an existing infrastructure, where Eclipse Riena and Hessian was used for communication. There it was possible and is used throughout the system. So I was hoping for a best-practice solutions, e.g. passing the type in case the type is 'Object'. – flavio.donze Jul 13 '19 at 08:12

1 Answers1

0

I found manged to configure the ObjectMapper to add the typing information:

ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);

There is also other typing configurations:

JAVA_LANG_OBJECT
This value means that only properties that have Object as declared type (including generic types without explicit type) will use default typing.

NON_CONCRETE_AND_ARRAYS
Value that means that default typing will be used for all types covered by OBJECT_AND_NON_CONCRETE plus all array types for them.

NON_FINAL
Value that means that default typing will be used for all non-final types, with exception of small number of "natural" types (String, Boolean, Integer, Double), which can be correctly inferred from JSON; as well as for all arrays of non-final types.

OBJECT_AND_NON_CONCRETE
Value that means that default typing will be used for properties with declared type of Object or an abstract type (abstract class or interface).

I also have to register a ParamConverterProvider which considers the ObjectMapper settings for all parameter passed:

@Provider
public class ObjectMapperParamConverterProvider implements ParamConverterProvider {

    @Override
    public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType, final Annotation[] annotations) {
        ObjectMapper mapper = ...;
        return new ParamConverter<T>() {
            @Override
            public T fromString(final String value) {
                try {
                    T result = mapper.readValue(value, rawType);
                    return result;
                } catch (IOException e) {
                    throw new ProcessingException(e);
                }
            }

            @Override
            public String toString(final T value) {
                try {
                    String result = mapper.writeValueAsString(value);
                    return result;
                } catch (JsonProcessingException e) {
                    throw new ProcessingException(e);
                }
            }
        };
    }
}
flavio.donze
  • 7,432
  • 9
  • 58
  • 91