Retrofit2: Expected BEGIN_ARRAY - android

I am using for first time of Retrofit2 and I got this error:
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
I know my problem is my pojo class but I can't find the right solution to my problem.I create WCF web service an this service return to me a Arraylist of my user class as a json. this is my jsom response from wcf server by poster:
{
"GetUsersResult": [
{
"CreationDateTime": "/Date(1508174478395)/",
"Id": "8e601956-04ab-4464-9f84-c129141b8198",
"Topic": "Topics",
"UserInfo": {
"Age": "15",
"Email": "",
"LastName": "choopani",
"Name": "niloofar",
"SimId": "89984320001079005854"
},
"UserName": "niloo"
},...
According my response I have created pojo class :
public class User {
#SerializedName("UserInfo")
private ExpChild mChildrenList;
private String UserName, CreationDateTime,Id,Topic;
public User(String mUserName,String mCreationDateTime,String mId,
String mTopic) {
UserName = mUserName;
CreationDateTime = mCreationDateTime;
Id = mId;
Topic = mTopic;
}
this is Interface for my client post method:
#POST("json/GetUsers")
Call<List<User>> getAllUsers();
as you can see, I want to be returned a list of my User.
after that I create retrofit service creator and into my main Activity I've used it:
allUsers.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
List<User> users = response.body();
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Toast.makeText(MainActivity.this, "faild to connect", Toast.LENGTH_SHORT).show();
Log.i("====>", "onFailure: "+ t.getMessage());
}
});
but I got above error!!where is my mistake?

Your server returns a JSON object, but your interface says it expects a JSON array. (The List type).
You need to have a second object to represent the full response by the server:
public class ServerResponse {
#SerializedName("GetUsersResult")
private List<User> users;
... Constructor and getters here
}
Then the Retrofit interface should return Call<ServerResponse> which matches the returned JSON, fixing the error.

Related

Parsing unsuccessfully JSON Objects with retrofit2

I am using retrofit2 in my code with gson.
i can parse almost all needed, but when parsing a specific JSON, I have a problem.
This is the JSON:
{
"data": [
{
"id": "001",
"direccion": "Cascada 3",
"provincia": "Burgos"
},
{
"id": "002",
"direccion": "Lago 21",
"provincia": "Zamora"
}
]
}
I know that what I am going to write has been commented a lot but, please, read until the end
When parsing it shows this error:
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
If I delete the name of the object “data” from JSON, then I can parse it perfectly, like this:
[
{
"id": "001",
"direccion": "Cascada 3",
"provincia": "Burgos"
},
{
"id": "002",
"direccion": "Lago 21",
"provincia": "Zamora"
}
]
The JSON continues to open with an object, but it can be parsed perfectly.
This leads me to think that I am not indicating in the code how to parse the object called "data", I think the problem is that.
Tiendas.java (I use #serializedName for a possible solution that I have read but it does not work)
import com.google.gson.annotations.SerializedName;
public class Tiendas {
#SerializedName("id")
private int id;
#SerializedName("provincia")
private String provincia;
#SerializedName("direccion")
private String direccion;
public int getId() {
return id;
}
public String getProvincia() {
return provincia;
}
public String getDireccion() {
return direccion;
}
}
The interface
#GET("tiendas/tiends.json")
Call<List<Tiendas>> getTiendas();
MainActivity.java
private void getGETsinParametros() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.MY_URL_BASE.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
JsonPlaceHolderApi jspAPI = retrofit.create(JsonPlaceHolderApi.class);
Call<List<Tiendas>> call = jspAPI.getTiendas();
call.enqueue(new Callback<List<Tiendas>>() {
#Override
public void onResponse(Call<List<Tiendas>> call, Response<List<Tiendas>> response) {
if (!response.isSuccessful()) {
Log.e(TAG, "Código de respuesta no exítoso:" + response.code());
} else {
Log.d(TAG,"response code:"+response.code());
List<Tiendas> listaObtenida = response.body();
for(Tiendas stores: listaObtenida){
String content = "";
content += "id" + stores.getId() + "\n";
content += "provincia" + stores.getProvincia() + "\n";
content += "direccion" + stores.getDireccion() + "\n";
jsonText.append(content);
}
}
}
#Override
public void onFailure(Call<List<Tiendas>> call, Throwable t) {
Log.e("TAG, "error:" + t.getMessage());
}
});
}
Can someone tell me how to fix the error with "data", or with what is necessary to make it work?
The JSONs are external, so I can't modify them.
Thanks and regards.
You need to create the wrapper for the Array object to get the list of tiendas:
public class Data{
private List<Tiendas> data;
public List<Tiendas> getData() {
return data;
}
}
And call it in your interface:
#GET("tiendas/tiends.json")
Call<Data> getTiendas();
do not forget to use the new object at onResponse:
List<Tiendas> listaObtenida = response.body().getData();
The error has nothing to do with Retrofit. For a JSON, a field is an object, [] is an array, {} is a POJO.
In your first JSON, you have a data object with type List(or Array). Therefore, you will need to create a POJO on top (Access Modifier is omitted for simplicity)
class DataClazz {
List<Tiendas> data;
}

Unexpected token # in JSON at position 0 Error on Android Studio using Retrofit2

I have a problem that "Unexpected token # in JSON at position 0".
What I wanna do is FirebaseUser delete using CloudFunction on Firebase.
There is no return but the Error says that "there is unexpected json token #"
This is some codes:
CloudFunctionsService( Interface )
public interface CloudFunctionsService {
#POST("deleteUser")
Call<Void> deleteUser(#Body String uid);
}
FunctionRetrofit ( RetrofitClass )
public class FunctionRetrofit {
private static FunctionRetrofit instance = new FunctionRetrofit();
public static FunctionRetrofit getInstance(){
return instance;
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
private CloudFunctionsService cfs = retrofit.create(CloudFunctionsService.class);
public CloudFunctionsService getService(){
return cfs;
}
}
function deleteUser ( on FirebaseCloudFunction )
exports.deleteUser = functions.https.onRequest((req, res) => {
if (req.body.uid === undefined) {
res.status(400).send('No user id defined');
} else {
var userId = req.body.uid;
admin.auth().deleteUser(userId)
.then(function() {
console.log("Successfully deleted user");
})
.catch(error=> {
console.log("Error deleting user: ", error);
});
res.status(200).end();
}
});
Code Executing ( Activity )
result is success but actually not changed anything
Call<Void> res = FunctionRetrofit.getInstance().getService().deleteUser(user.getUid());
res.enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) {
Log.d("success", "suceess");
}
#Override
public void onFailure(Call call, Throwable t) {
Log.e("Error", t.getMessage().toLowerCase());
}
});
Error
SyntaxError: Unexpected token # in JSON at position 0
at JSON.parse (<anonymous>)
at createStrictSyntaxError (/worker/node_modules/body-parser/lib/types/json.js:157:10)
at parse (/worker/node_modules/body-parser/lib/types/json.js:83:15)
at /worker/node_modules/body-parser/lib/read.js:121:18
at invokeCallback (/worker/node_modules/raw-body/index.js:224:16)
at done (/worker/node_modules/raw-body/index.js:213:7)
at IncomingMessage.onEnd (/worker/node_modules/raw-body/index.js:273:7)
at emitNone (events.js:106:13)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
As you are using the GsonConverterFactory, I think it's expecting json (or to serialize to JSON) when you use the #Body annotation. As you are passing a raw String value I think this is where it errors.
Please disregard the answer above. The GsonConverterFactory will serialise your own Type to JSON, however you are sending in a raw String value. This will not be serialized so the body of the post for an id of 3 will be "3" - I think the api you are calling for deleteUser is expecting JSON in the body which you are not sending which is why you are getting the error. I would check the docs of the Firebase API call you are making to see what format it expects the post body to be in. It is more likely to be something like:
{
"userId": "3"
}
If this is the case then you would need a class like:
public class User {
private String userId;
public User(String userId){
this.userId = userId;
}
}
You are currently sending a " character as the first character when it's probably expecting a { to signify starting a JSON object

How can I get one single object from a Query using Retrofit 2?

I want to get a User as JSONObject from a JSONArray. He has email as a unique value in database.
If I make a call like this:
#GET("users")
Call<List<User>> getUser(#Query("email") String email);
I have to receive the data that way:
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(#NonNull Call<List<User>> call,
#NonNull Response<List<User>> response) {
List<User> users_list = response.body();
if (users_list != null) {
User user = users_list.get(0);
}
}
#Override
public void onFailure(#NonNull Call<List<User>> call, #NonNull Throwable t) {
}
});
Because my call returns a list of only one element. Is there any other way to get the object directly, not being in a list?
The response I get is like:
[
{
"id": 17,
"email": "myemail#gmail.com",
...
}
]
And I want to get just:
{
"id": 17,
"email": "myemail#gmail.com",
...
}
If I replace
List<User> with User
in GET method and in the call it won't work. I'm looking for some method like FirstOrDefault() from .NET Core 2 in C# or something similar. Thanks!

Retrofit List of object assigns null to data

This is how my JSON response looks
[
{
"id": 1,
"PhName": "sample string 2",
"Longitude": 3.1,
"Latitude": 4.1,
"ApplicationUserId": "sample string 5"
},
{
"id": 1,
"PhName": "sample string 2",
"Longitude": 3.1,
"Latitude": 4.1,
"ApplicationUserId": "sample string 5"
}
]
this is my retrofit interface call
#GET("/api/GPS")
#Headers({
"Accept: application/json"
})
Call<List<UserResponse>> search(#Query("search") String search,
#Header("Authorization") String auth);
Pojo Class
public class UserResponse {
#SerializedName("Id")
int id;
#SerializedName("UserName")
String phName;
#SerializedName("Longitude")
int lon;
#SerializedName("Latitude")
int lat;
#SerializedName("ApplicationUserId")
String appUserId;
//getters and setters
}
Retrofit declaration
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(PerformLogin.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Getting data and using it
MyApiEndpointInterface apiService =
retrofit.create(MyApiEndpointInterface.class);
Call<List<UserResponse>> call = apiService.search(query,"Bearer "+token);
call.enqueue(new Callback<List<UserResponse>>() {
#Override
public void onResponse(Call<List<UserResponse>> call, Response<List<UserResponse>> response) {
List<UserResponse> userList = response.body();
Log.w("My App", response.message());
for (int i = 0; i <userList.size() ; i++) {
Log.w("My App", userList.get(i).getPhName()+""+i);
}
//mAdapter = new MyAdapter(getContext(),userList);
//mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onFailure(Call<List<UserResponse>> call, Throwable t) {
Log.w("My App", t.getMessage());
}
});
my response
W/My App: OK
W/My App: null 0
W/My App: null 1
W/My App: null 2
W/My App: null 3
In this case I am suppose to receive four result from the search and the names are giving me null.
Is there anything I am doing wrong or a better solution to this?
You are using wrong serialize name. You are trying to assign value from node UserName to phName, which is not available. So, you are getting null.
Change
#SerializedName("UserName")
String phName;
with
#SerializedName("PhName") // change this
String phName;
Also, #SerializedName("Id") should be #SerializedName("id"). It's case sensitive.
Your SerializedName fields and the JSON fields don't match.
JSON: id -> GSON: Id
JSON: PhName -> GSON: UserName
Those two don't add up. You have to alter your annotations accordingly:
#SerializedName("id")
int id;
#SerializedName("PhName")
String phName;

Custom retrofit json deserializer

I've been using Retrofit library recently and I like it so far. However, I've come across API that returns response with the following structure:
"data":
{
"meta":
{
"code": 200
"data": "data"
},
"body": [
{
"id": "id",
"field1": "data",
"field2": "data"
},
{
"id": "id",
"field1": "data",
"field2": "data"
}]
}
Assuming I have following class:
public class Entity {
long id;
String field1;
String field2;
}
and following interface:
public interface EntityService {
#GET("/api/method")
void getEntities(Callback<List<Entity>> callback);
}
what is the correct way to use Retrofit if I want to get a list of Entity objects from data.body element from response? I know I could have passed Response object in the callback and manually parse it using org.json package or something else, but is there any better way?
Why not just make a class that represents the data like you've already half done? That's what I've done in multiple apps along with other developers:
public class Meta {
String code;
String data;
}
public class Data {
Meta meta;
List<Entity> body;
}
public interface EntityService {
#GET("/api/method")
void getEntities(Callback<Data> callback);
}
It seems that extra data in your result could be re-used. You should use a generic class like this one:
public class Data<E> {
Meta meta;
E body;
public static class Meta {
int code;
String data;
}
}
Then you could use
Data<List<Entity>>
or whatever you want:
public interface EntityService {
#GET("/api/method")
void getEntities(Callback<Data<List<Entity>>> callback);
}

Categories

Resources