4
I’m studying Webservice and consumption on Android with Retrofit2.
I’ve done tests with public Apis like Viacep and FIPE and I can use the retrofit quietly, very easy but when I set up my own webservice using JAX-RS with Jersey I have problems. I’ll post the details so you can help me if you can.
The error message is:
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2
Result when calling the resource in Webservice - JSON file:
URL: http://MEUSERVER:8080/movie/movie rentals
{
"filmes": {
"filme": [
{
"id": 1,
"titulo": "E o vento levou",
"ano": "1961-01-12T00:00:00-03:00",
"idioma": "Português",
"atorPrincipal": "ValdikSoriano",
"locado": false,
"valorDiaria": 2.65
},
{
"id": 2,
"titulo": "Titanic",
"ano": "1998-01-12T00:00:00-02:00",
"idioma": "Português",
"atorPrincipal": "Dicaprio",
"locado": false,
"valorDiaria": 2.65
}
]
}
}
My web.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>LocadoraFilmes</display-name>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>service.ApplicationJAXRS</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
My file ApplicationJAXRS.java:
@ApplicationPath("resources")
public class ApplicationJAXRS extends Application {
@Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(new JettisonFeature());
return singletons;
}
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(FilmeService.class);
return classes;
}
}
The project I did is using JPA, so there’s JPA Annotation in addition to JAX-RS:
Filing cabinet Filme.java:
@Entity(name = "filme")
@Table(name = "filme")
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Filme implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@XmlElement(name = "id")
private Integer id;
@Column(nullable = false)
private String titulo;
@Column(nullable = false)
@Temporal(TemporalType.DATE)
private Date ano;
@Column(nullable = false)
private String idioma;
@Column(nullable = false)
private String atorPrincipal;
@Column(nullable = false)
private Boolean locado = false;
@Column(nullable = true)
private Float valorDiaria;
public Filme() {
}
public Filme(Integer id, String titulo, Date ano, String atorPrincipal, Boolean locado, Float valorDiaria) {
this.id = id;
this.titulo = titulo;
this.ano = ano;
this.atorPrincipal = atorPrincipal;
this.locado = locado;
this.valorDiaria = valorDiaria;
}
//GET and SET
}
Filing cabinet FilmeService.java:
@Path("/filmes")
@Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8"})
@Consumes({MediaType.APPLICATION_JSON + ";charset=UTF-8"})
public class FilmeService {
private FilmeDAO daoFilme = new FilmeDAO();
@GET
public List<Filme> listarFilmes(){
return daoFilme.findAll();
}
@GET
@Path(value = "{id}")
public Filme getFilmeByID(@PathParam(value="id")int id){
return daoFilme.findById(id);
}
}
On Android device, as already mentioned, I use the retrofit. Here’s the file FilmeAPI.java there is there:
public interface FilmeAPI {
@GET("filmes")
Call<List<Filme>> getFilmes();
//Gson gson = new GsonBuilder().registerTypeAdapter(Filme.class, new FilmeDeserializer()).create();
Gson gson = new GsonBuilder().setLenient().create();
public static final Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.25.2:8080/locadorafilmes-0.0.1-SNAPSHOT/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
The class Filme.java on android:
public class Filme implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private String titulo;
private Date ano;
private String idioma;
private String atorPrincipal;
private Boolean locado = false;
private Float valorDiaria;
//GET and SET
}
And the file MainActivity.java:
public class MainActivity extends AppCompatActivity {
private ListView lvFilmes;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lvFilmes = (ListView) findViewById(R.id.listview_filmes);
connectWebServiceFilmes();
}
private void connectWebServiceFilmes(){
FilmeAPI filmeAPI = FilmeAPI.retrofit.create(FilmeAPI.class);
Call<List<Filme>> callFilme = filmeAPI.getFilmes();
callFilme.enqueue(new Callback<List<Filme>>() {
@Override
public void onResponse(Call<List<Filme>> call, Response<List<Filme>> response) {
Log.i("Teste", "Dentro do onResponse");
List<Filme> filmes = new ArrayList<Filme>();
if (response.body()!=null){
Log.i("Teste", "Response não esta vazio! " + response.isSuccessful());
filmes.addAll(response.body());
if (filmes!=null){
onUpdateListViewFilmes(getBaseContext(), filmes);
}
else{
Log.e("Teste", "Array de Filmes vazio!");
}
}
}
@Override
public void onFailure(Call<List<Filme>> call, Throwable t) {
Log.e("Teste", "Erro ao baixar dados. Mensagem: " + t.getMessage() +
" \n Local Mensagem: " + t.getLocalizedMessage() +
" \n TrackTrace: " + t.getStackTrace());
}
});
}
private void onUpdateListViewFilmes(Context context, List<Filme> filmes){
FilmeAdapter adapter = new FilmeAdapter(context, filmes);
lvFilmes.setAdapter(adapter);
}
}
The problem seems to be in the structure of your JSON. The client expects an array but receives an object
filmes, being that it is inside that object that the array isfilme. Which server are you using? A user on Soen said they faced a problem when using Jersey 2.x with Glassfish 3.x:link– Felipe Marinho
I’m wearing Wildfly 10
– Hugo