0

CustomObjectResource - rest service returning a simple POJO with text, long and local date time fields.

@Component
@Path("/resource")
public class CustomObjectResource {

    private RandomCOBuilder randomCOBuilder = new RandomCOBuilder();

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response getCustomObject(@Context HttpServletRequest httpRequest) {
        String acceptHeader = httpRequest.getHeader("Accept");//I do not use it in the code. When I debug, this param is correct.
        CustomObject customObject = randomCOBuilder.get();
        return Response
                // Set the status and Put your entity here.
                .ok(customObject)
                // Add the Content-Type header to tell Jersey which format it should marshall the entity into.
                .build();
    }
}

And this is my Postman. Part -1 accept=json Status code is 200, the JSON parsing fails. Actually, the object returned is in XML. When I choose to display the result in XML,

<customObject>
    <id>5</id>
    <text>CustomObject_5</text>
    <timestamp>2017-08-07 17:17:40</timestamp>
</customObject>

Now, I use Accept:application/xml

Part-2 accept xml

It does not return anything: 404.

I use SpringBoot with Jackson.

This is my gradle.build

group 'com.ca.training.rest'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile "org.springframework.boot:spring-boot-starter-jetty:1.5.3.RELEASE"
    compile "org.springframework.boot:spring-boot-starter-web:1.5.3.RELEASE"

    compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.8"
    compile "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.8.8"

    compile 'javax.ws.rs:javax.ws.rs-api:2.0.1'
    compile "org.springframework.boot:spring-boot-starter-jersey:1.5.3.RELEASE"

    testCompile 'junit:junit:4.12'
}

CustomObject

@XmlRootElement(name = "customObject")
@JsonRootName(value = "customObject")
public class CustomObject {

    private Long id;
    private String text;

    @JsonFormat(pattern = DATE_FORMAT)
    @XmlJavaTypeAdapter(DateTimeAdapter.class)
    private LocalDateTime timestamp;

}

Update

I was debugging. In my code everything works well... But when I debugged further:

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "timestamp"
    this problem is related to the following location:
        at public java.time.LocalDateTime com.ca.training.rest.example.core.entity.CustomObject.getTimestamp()
        at com.ca.training.rest.example.core.entity.CustomObject
    this problem is related to the following location:
        at private java.time.LocalDateTime com.ca.training.rest.example.core.entity.CustomObject.timestamp
        at com.ca.training.rest.example.core.entity.CustomObject
  • (1) [Try this to debug. See if there is any error](https://stackoverflow.com/a/31289875/2587435). (2) [Also set this property](https://stackoverflow.com/a/36600434/2587435). (3) [Also consider this (as the Jersey starter pulls in jersey-media-json-jackson)](https://stackoverflow.com/a/38016814/2587435). – Paul Samsotha Aug 07 '17 at 22:13
  • @peeskillet There is a log message `2017-08-08 10:21:58.048 WARN 8436 --- [qtp620412175-32] o.e.jetty.server.handler.ErrorHandler : Error page loop /error` –  Aug 08 '17 at 08:23
  • That's a warning, not an error, but it looks related to #2 above. – Paul Samsotha Aug 08 '17 at 08:26
  • @peeskillet When I debug, there is no exception in the controller's (resource) logic. It feels like Spring has a bug. –  Aug 08 '17 at 08:30
  • Yeah, I'm not really sure. I have no idea how these things could happen as you've described. Just making some guesses. – Paul Samsotha Aug 08 '17 at 08:34
  • @peeskillet I found an exception, I updated my question. –  Aug 08 '17 at 08:53
  • The error is saying something about the CustomObject class. You will need to post the entire class, or at least everything related to the timestamp, as that's what the error is about – Paul Samsotha Aug 08 '17 at 08:57
  • 1
    https://stackoverflow.com/questions/12392235/illegalannotationsexception-class-has-two-properties-of-same-name '@XmlAccessorType(XmlAccessType.FIELD)' To the class.https://stackoverflow.com/questions/12392235/illegalannotationsexception-class-has-two-properties-of-same-name – Yan Khonski Aug 08 '17 at 09:00

1 Answers1

0

This is the solution.

1) @XmlAccessorType(XmlAccessType.FIELD) to get rid of the error. Otherwise it picks up properties from fields and getters.

@XmlRootElement(name = "customObject")
@JsonRootName(value = "customObject")
@XmlAccessorType(XmlAccessType.FIELD)
public class CustomObject {

    private Long id;
    private String text;

    @JsonFormat(pattern = DATE_FORMAT)
    @XmlJavaTypeAdapter(DateTimeAdapter.class)
    private LocalDateTime timestamp;

    //Getters and setters
}

2) In JerseyConfig add Jackson config

@Component
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        registerEndpoints();
    }

    private void registerEndpoints() {
         register(CustomObjectResource.class);
         register(JacksonConfig.class);//I need this
    }
}

3) And Jackson config

@Provider
public class JacksonConfig implements ContextResolver<ObjectMapper> {

    private ObjectMapper objectMapper = objectMapper();

    private ObjectMapper objectMapper() {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        objectMapper = builder.build();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
        objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
        return objectMapper;
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return objectMapper;
    }
}

Now it works and returns the object in both XML and JSON.

Accept:application/xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customObject>
    <id>2</id>
    <text>CustomObject_2</text>
    <timestamp>2017-08-08 11:49:47</timestamp>
</customObject>

Accept:application/json

{
    "customObject": {
        "id": 3,
        "text": "CustomObject_3",
        "timestamp": "2017-08-08 11:50:25"
    }
}