REST call returning blank XML

2019-09-11 02:07发布

问题:

I am pretty new to REST programming.

Below is my class which is intended to return XML/JSON but I am having some difficulties to make it return proper values. I tried returning Response, JsonArray and Object of my POJO class but its not working. I looked several threads but not able to figure out what exactly is the problem.

The resource class:

public class UserService {

    UserDBHandler userDBHandler; 
    Friend f;

    @GET
    @Path("users/{userId}/friends")
//  @Produces(MediaType.TEXT_PLAIN)
    @Produces({ MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})
    public Friend  getFriends(@PathParam("userId") String userId) throws SQLException, ClassNotFoundException
    {
        System.out.println("userId : " +userId);
        userDBHandler =  new UserDBHandler();
        f = new Friend();



        ArrayList<String> userList = userDBHandler.fetchUsers(userId);

        System.out.println("Array size: "+userList.size());
        Iterator<String> iterator = userList.iterator();

        while(iterator.hasNext())
        {
            f.setUri(iterator.next());
            System.out.println(f.getUri());
        }

        //JsonObject  object = Json.createObjectBuilder().add("frienduri",f.getUri()).build();
        //ResponseBuilder response = Response.ok(f);

        //return Json.createArrayBuilder().add(object).build()
        //return response.build();
        return f;
    }
}

The POJO class:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;

@XmlRootElement
public class Friend{

    private String friendURI;
    private String event;
    private String uri;

    String getUri() {
        return uri;
    }
    void setUri(String uri) {
        this.uri = uri;
    }
    String getFriendURI() {
        return friendURI;
    }
    void setFriendURI(String friendURI) {
        this.friendURI = friendURI;
    }
    String getEvent() {
        return event;
    }
    void setEvent(String event) {
        this.event = event;
    }
}

This is what I get when I return the Response or the Friend object:

**<?xml version="1.0" encoding="UTF-8" standalone="yes"?><friend/>**

And when returning JsonArray this is what I get:

[{"frienduri":{"string":"b@b.com","chars":"b@b.com","valueType":"STRING"}}]

Another problem I am facing is: if I create a constructor I get below error:

A MultiException has 1 exceptions. They are: 1. java.lang.NoSuchMethodException: Could not find a suitable constructor in repository.resources.UserService class.

My dev environment is Tomcat 8 , JDK 1.8, Eclipse Luna.

I am not using Maven or web.xml instead I have an Application class.

I am using jaxrs-ri-2.13 and jackson jars:

  • jackson-core-asl-1.9.2
  • jackson-jaxrs-1.9.2
  • jackson-mapper-asl-1.9.2
  • jackson-xc-1.9.2

Json jars

  • javax.json-api-1.0
  • javax.json-1.0.4

Thanks

回答1:

First thing you should understand is that JAX-RS is a specification, and that jar you have jaxrs-ri-2.13 is an implementation (actually the reference implementation) from Jersey. Knowing the implementation is important in helping to solve your problem. So you should mention this in your post (through a tag or explicitly).

Second, for serialization, JAX-RS requires MessageBodyReaders and MessageBodyWriters. Jersey distribution has support for xml out the box, but for JSON we need to add a module with for support. Jackson by itself does not provide support for this. We need a provider module like jersey-media-jason-jackson. Using Maven is the easiest way to get this module, as it is dependendent on other jars. If I create a new Maven project, with only this dependency, this is the complete list, of artifacts the the module is dependent on

The Jersey distribution has some of these artifacts already. I am not sure if all of them are included in the bundle jar, but based on the main (un bundled) Jersey distribution, the un-shaded jars are not included

You will need to go looking for those jars and add them to your project. Like I said, I don't know if the Jersey Bundle jar comes with the others, packages, bu you can get the complete distribution here that uses individual jars. If using the single jar doesn't work, I would look into using the separate jars (all of them).

Adding these jars should give you Friend to JSON support.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?><friend/>

You need to make your Friend accessor methods public. The writer cannot find the getXxx method as it's not in the same package. You should also make the setXxx methods public.


Another problem I am facing is: if I create a constructor I get below error:

A MultiException has 1 exceptions. They are: 1. java.lang.NoSuchMethodException: Could not find a suitable constructor in repository.resources.UserService class

Yup. The resource class is created by Jersey, which handles the lifecycle of our request scoped resource classes, and requires a no-arg constructor. Adding a constructor with arguments, cancels the implicit no-arg constructor. But even if you create a no-arg constructor and you other arg constructor, the arg constructor will never be called, unless you explicitly create the instance of your resource class as a singleton.


UPDATE

Regarding the singelton instance, I did not find the same in tutorials. Can you please tell me the way to do that?

When you extend Application, you can override getClasses() (which will be container managed request scoped resource class) and getSingletons which will be a single instance for the entire application

@ApplicationPath("..")
public class MyApplication extends Application {
    private Set<Class<?>> classes = new HashSet<>();
    private Set<Object> singletons = new HashSet<>();

    public MyApplication() {
        singletons.add(new UserResource(new UserDBHandler()));
    }

    @Override
    public Set<Class<?>> getClasses() {
        return classes;
    }

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}

But I am not sure if you really want UserResource to be a singleton. This means that you want this resource to be stateful for the entire application, which may not be desired. Instead you can use an injection framework, supported by Jersey, in order to inject the UserDBHandler into the UserResource class. Something like

@Path("...")
public class UserResource {
    @Inject
    private UserDBHandler handler;
}

@ApplicationPath("..")
public class MyApplication extends Application {
    private Set<Class<?>> classes = new HashSet<>();

    public MyApplication() {
        classes.add(UserResource.class);
        singletons.add(new AppBinder());
    }

    @Override
    public Set<Class<?>> getClasses() {
        return classes;
    }

    class AppBinder extends org.glassfish.hk2.utilities.binding.AbstractBinder {
        @Override
        public void configure() {
            bind(UserDBHandler.class).to(UserDBHandler.class);
        }
    }
}

See more at Custom Injection. Also I just tested this with the single jaxrs-rs jar and it does not come with the needed classes. You will need to download the separate Jersey distribution (mentioned above) will all the single jars, and add all those jars to your project, for this to work, or at least include the hk jars, which are also required by the Jackson provider anyway.