Search code examples
javarestjerseyjax-rs

MessageBodyWriter not found for media type=application/xml, type=class java.util.HashMap$Values


My API method couldn't convert object to XML but it can converts to JSON. when the method return type is Collection (MAP) it converts to XML without any runtime exceptions (MessageBodyWriter not found) but when use Response as return type it causes the runtime exceptions. here I mention the codes. I think I don't have any issues in my code but problems on dependencies.

package lk.ac.jfn.vau.MyDBapi.Model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Department {
    private long Id;
    private String Name;
    private String Location;
    
    public Department() {
        
    }
    
    public Department(long id, String name, String location) {
        super();
        Id = id;
        Name = name;
        Location = location;
    }

    public long getId() {
        return Id;
    }

    public void setId(long id) {
        Id = id;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getLocation() {
        return Location;
    }

    public void setLocation(String location) {
        Location = location;
    }
    
    

}

package lk.ac.jfn.vau.MyDBapi.Repo;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Repo {
    private static final String DB_Driver = "com.mysql.jdbc.Driver";
    private static final String DB_Connection = "jdbc:mysql://localhost:3306/";
    private static final String DB_Name = "departmentapi";
    private static final String DB_User = "root";
    private static final String DB_Password = "";

    public static void getDriver() {
        try {
            Class.forName(DB_Driver);
            System.out.println("Driver found");
        } catch (ClassNotFoundException e) {
            System.out.println("Driver not found " + e.getMessage());
        }
    }

    public static Connection getConnection() {
        Connection connection = null;

        try {
            connection = DriverManager.getConnection(DB_Connection + DB_Name, DB_User, DB_Password);
            System.out.println("Database Connected");
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }

        return connection;
    }

    public ResultSet get(String query, Connection connection) {
        ResultSet resultSet = null;
        try {
                Statement statement = connection.createStatement();
                resultSet = statement.executeQuery(query);

        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
        return resultSet;
    }
}

package lk.ac.jfn.vau.MyDBapi.Repo;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import lk.ac.jfn.vau.MyDBapi.Model.Department;

public class DepartmentRepo extends Repo {
    Connection connection=null;
    public DepartmentRepo() {
        getDriver();
        connection = getConnection();
    }

    Map<Long, Department>map=new HashMap<Long, Department>();
    
    public Collection<Department> getAll(){
        ResultSet resultSet=get("select * from department", connection);
        try {
            while(resultSet.next()) {
                Department department=new Department(resultSet.getLong(1), resultSet.getString(2), resultSet.getString(3));
                map.put(department.getId(), department);
            }
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
        
        return map.values();
    }
    
}

package lk.ac.jfn.vau.MyDBapi;

import java.util.Collection;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import lk.ac.jfn.vau.MyDBapi.Model.Department;
import lk.ac.jfn.vau.MyDBapi.Repo.DepartmentRepo;

@Path("/dept")
@Produces(value = {MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes(MediaType.APPLICATION_JSON)
public class DepartmentResource {
    private DepartmentRepo repo=new DepartmentRepo();
    @GET
    public Response getDepartments() {
        Collection<Department> departments = repo.getAll();
        if(!departments.isEmpty()) {
            return Response.status(Status.FOUND).entity(departments).build();
        }
        return Response.status(Status.NOT_FOUND).entity("Nothing Found").build();
    }

}

POM.xml

<dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <!-- use the following artifactId if you don't need servlet 2.x compatibility -->
            <!-- artifactId>jersey-container-servlet</artifactId -->
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-json-jackson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
        </dependency>
    </dependencies>

Solution

  • With JAXB (XML provider), you need to use GenericEntity.

    GenericEntity<Collection<Department>> entity
        = new GenericEntity<Collection<Department>>(departments){};
    return Response.ok(entity).build();
    

    The reason you need to do this is because JAXB needs to know the type information in order to serialize. When you just return the generic entity, as opposed to Response, the generic type information is in the method signature

    @GET
    public Collection<Department> getDepartments() {}
    

    When you use Response, generic information is not available due to type erasure. So what JAX-RS allows is to use GenericEntity. But you cannot just use an instance, because type erasure does not allow for this. You actually need to use an anonymous class

    new GenericEntity<Collection<Department>>(departments){}
    

    Notice the curly braces at the end. This is an anonymous class. Through reflection, you are able to get generic type parameters from this anonymous class.