I am using android-bootsrap from this link for my project from
https://github.com/AndroidBootstrap/android-bootstrap
I retained the project structure as it is and not performing authentication. In my apps i am making a call to a rest API which is returning the data in Json format. Using retrofit we are calling the services and getting the data in Json also.
We have created one POJO class for Json API. But in my app we are unable to get the Pojo object populated with Rest API data.
My code look as follows:
User.java
public class User implements Serializable {
private String shortDescription;
private String name;
public String getShortDescription() { return shortDescription;}
public void setShortDescription(String shortDescription) {
this.shortDescription = shortDescription;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
UserService.java
public interface UserService {
#GET("/search/items")
UsersWrapper getUsers();
}
UserWrapper.java
public class UsersWrapper {
private User results;
public User getResults() { return results; }
}
MainActivity.class
public class MainActivity extends BaseActivity {
String data = "";
private User user;
#Inject protected BootstrapServiceProvider serviceProvider;
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyTask().execute();
}
// making async request
class MyTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
try {
user = serviceProvider.getService().getUsers();
user.getShortDescription(); // here i am getting NullPointerException
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
}
I am getting the data in the retrofit log but when i am trying to get data through User object i am getting NullPointerException.
My Json data is as follows:
{"name":"user1","short_description":"user1 is a postgraduate student"}
So, the way you have your User/UserWrapper class setup GSON is expecting a JSON result that looks like:
{
"results": {
"name": "user1",
"short_description": "user1 is a postgraduate student"
}
}
If the JSON Result is just the stuff inside of the "results" block in my above example you would want to ditch the UserWrapper all together, and change your Service Interface to look like:
public interface UserService {
#GET("/search/items")
User getUsers();
}
If the JSON Result is an Array of Users such as
[
{
"name": "user1",
"short_description": "user1 is a postgraduate student"
},
...
]
Then you would want your Interface to look like:
public interface UserService {
#GET("/search/items")
List<User> getUsers();
}
Related
How should I create a class for an api response that returns objects instead of a list of objects? I'm using Spring Boot and RestTemplate and don't know how to go about it.
This is a response:
{
"status":200,
"data":{
"1":{
"id":"1",
"auth":"manual",
"confirmed":"1",
"policyagreed":"0",
"deleted":"0",
"suspended":"0",
"mnethostid":"1",
"username":"guest",
"password":"",
"idnumber":"",
"firstname":"Guest user",
"lastname":" ",
"email":"root#localhost",
"emailstop":"0",
"icq":"",
"skype":"",
"yahoo":"",
"aim":"",
"msn":"",
"phone1":"",
"phone2":"",
"institution":"",
"department":"",
"address":"",
"city":"",
"country":"",
"lang":"en",
"calendartype":"gregorian",
"theme":"",
"timezone":"99",
"firstaccess":"0",
"lastaccess":"0",
"lastlogin":"0",
"currentlogin":"0",
"lastip":"",
"secret":"",
"picture":"0",
"url":"",
"description":"",
"descriptionformat":"1",
"mailformat":"1",
"maildigest":"0",
"maildisplay":"2",
"autosubscribe":"1",
"trackforums":"0",
"timecreated":"0",
"timemodified":"1584114527",
"trustbitmask":"0",
"imagealt":null,
"lastnamephonetic":null,
"firstnamephonetic":null,
"middlename":null,
"alternatename":null,
"moodlenetprofile":null
},
"2":"...."
}
}
I tried something like this:
public class MoodleResponse {
private Integer status;
private Data data;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
public class Data {
private HashMap<String, MoodleUser> map;
public HashMap<String, MoodleUser> getMap() {
return map;
}
public void setMap(HashMap<String, MoodleUser> map) {
this.map = map;
}
}
It doesn't work. I've never encountered anything like this.
you will need to create a model class here which has all attributes present in response and each attribute must be mapped with #JsonProperty
key here is to understand #JsonPropery annotation
for eg
public class Data{
#JsonProperty("id")
private String id;
#JsonProperty("auth")
private String auth;
}
and soo on
I've been trying to create a nested RealmObject using a json but it only creates the first Object and not the nested ones. I would appreciate a help on this.
my Realm classes:
Content.java
public class Content extends RealmObject {
private String uuid;
RealmList<ContentDetailModel> ContentDetail;
public Content() {
super();
this.uuid = UUID.randomUUID().toString();
}
public String getUuid() {
return uuid;
}
public RealmList<ContentDetailModel> getContentDetails() {
return ContentDetail;
}
public void setContentDetails(RealmList<ContentDetailModel> contentDetails) {
this.ContentDetail = contentDetails;
}
}
ContentDetailModel.java:
public class ContentDetailModel extends RealmObject {
String FileName;
String ContentTypeID;
RealmList<ContentDetailMetadataModel> ContentDetailMetadata;
RealmResults<Content> content = null;
public String getFileName() {
return FileName;
}
public void setFileName(String fileName) {
FileName = fileName;
}
public String getContentTypeID() {
return ContentTypeID;
}
public void setContentTypeID(String contentTypeID) {
ContentTypeID = contentTypeID;
}
public RealmList<ContentDetailMetadataModel> getContentDetailMetadata() {
return ContentDetailMetadata;
}
public void setContentDetailMetadata(RealmList<ContentDetailMetadataModel> contentDetailMetadataz) {
this.ContentDetailMetadata = contentDetailMetadataz;
}
}
and the rest of nested classes are like these. my Json string is as follows:
"
{
"Content":{
"ContentDetail":[
{
"FileName":"test.mp3",
"ContentTypeID":3,
"ContentDetailMetadata":{
"Metadata":[
{
"ID":2,
"Value":"2017-08-02 09:40:30"
},
{
"ID":1,
"Value":"35.73876557934912,51.50785446166992"
}
]
}
},
{
"FileName":"2.jpg",
"ContentTypeID":2,
"ContentDetailMetadata":[
{
"Metadata":{
"ID":2,
"Value":"2017-08-02 09:40:30"
}
},
{
"Metadata":{
"ID":1,
"Value":"35.73876557934912,51.50785446166992"
}
}
]
}
]
}
}
"
and the code I use to do it is :
realm.createObjectFromJson(json)
{
"Content":{
"ContentDetail":[
{
"FileName":"test.mp3",
"ContentTypeID":3,
"ContentDetailMetadata":[{
"Metadata":[
{
"ID":2,
"Value":"2017-08-02 09:40:30"
},
{
"ID":1,
"Value":"35.73876557934912,51.50785446166992"
}
]
}]
},
Translates to:
public class Root extends RealmObject {
private Content Content;
}
public class Content extends RealmObject {
private RealmList<ContentDetail> ContentDetail;
#LinkingObjects("Content")
private final RealmResults<Root> roots = null;
}
public class ContentDetail extends RealmObject {
private String FileName;
private long ContentTypeID;
//private ContentDetailMetadata ContentDetailMetadata;
private RealmList<ContentDetailMetadata> ContentDetailMetadata;
#LinkingObjects("ContentDetail")
private final RealmResults<Content> contents = null;
}
public class ContentDetailMetadata extends RealmObject {
private RealmList<Metadata> Metadata;
#LinkingObjects("ContentDetailMetadata")
private final RealmResults<ContentDetail> contentDetails = null;
}
public class Metadata extends RealmObject {
private long ID;
private String Value;
#LinkingObjects("Metadata")
private final RealmResults<ContentDetailMetadata> contentDetailMetadatas = null;
}
If your schema doesn't look like that, then createOrUpdateFromJson() won't work.
Personally I would advise against using this schema though, it's pretty bad as a Realm schema. It's advisable to parse the JSON and then map it into a schema that makes more sense!
It looks like your JSON has put all fields for the Content object under the Context JSON object instead of directly under the top-level object where it should be.
If you do this, it should work:
realm.createObjectFromJson(Content.class, json.getJSONObject("Content"));
To explain the problem I'm dealing with I will first provide the code.
RecipeController
#RequestMapping(path = "/addrecipe")
public void addNewRecipe(#RequestBody AddRecipeDto addRecipeDto){
Recipe newRecipe = new Recipe();
EvaUser user = evaUserRepository.findOne(addRecipeDto.getUserId());
for(Ingredient ingredient: addRecipeDto.getIngredients()){
ingredientRepository.save(ingredient);
}
newRecipe.setTitle(addRecipeDto.getTitle());
newRecipe.setAuthor(user);
newRecipe.setDescription(addRecipeDto.getDescription());
newRecipe.setIngredients(addRecipeDto.getIngredients());
recipeRepository.save(newRecipe);
user.getMyRecipes().add(newRecipe);
evaUserRepository.save(user);
}
UserController
#RequestMapping("/getusers")
public Iterable<EvaUser> getAllUsers() {
return evaUserRepository.findAll();
}
EvaUser
#OneToMany
private List<Recipe> myRecipes;
#ManyToMany
private List<Recipe> favoriteRecipes;
Recipe
#ManyToOne
private EvaUser author;
Exception
Failed to write HTTP message:
org.springframework.http.converter.HttpMessageNotWritableException: Could
not write content: Infinite recursion
Problem
So when I call the method to add a recipe, I want the database to know that there is a new recipe and that the new recipe is linked to the user who added it. When I drop the part where I save the user-entity, the mapping isn't made at all. But when I use the userRepository to tell the database that there has been made a change (adding the recipe to their list) it seems like there is an infinite loop of adding new users.
Answering to your question and including the last requirements from your comments.
If you want to break the loop, but some somehow want to keep also nested objects, I would recommend to write a custom serializer and replace the the object which causes the endless recursion with some other field (I used author username which is String instead of Author object in the example below).
To reproduce the case I created a mock model which is similar to yours.
Recipe:
public class Recipe {
private EvaUser author;
private String name = "test";
private String ingridients = "carrots, tomatos";
public EvaUser getAuthor() {
return author;
}
public void setAuthor(EvaUser author) {
this.author = author;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIngridients() {
return ingridients;
}
public void setIngridients(String ingridients) {
this.ingridients = ingridients;
}
}
EvaUser:
public class EvaUser {
private List<Recipe> myRecipes = new ArrayList<>();
private List<Recipe> favoriteRecipes = new ArrayList<>();
private String username;
public List<Recipe> getMyRecipes() {
return myRecipes;
}
public void setMyRecipes(List<Recipe> myRecipes) {
this.myRecipes = myRecipes;
}
public List<Recipe> getFavoriteRecipes() {
return favoriteRecipes;
}
public void setFavoriteRecipes(List<Recipe> favoriteRecipes) {
this.favoriteRecipes = favoriteRecipes;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Creating a custom serializer:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.util.Optional;
public class RecipeSerializer extends StdSerializer<Recipe> {
protected RecipeSerializer() {
this(null);
}
protected RecipeSerializer(Class<Recipe> t) {
super(t);
}
#Override
public void serialize(Recipe recipe, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("name", recipe.getName());
gen.writeStringField("author", Optional.ofNullable(recipe.getAuthor().getUsername()).orElse("null"));
gen.writeStringField("ingridients", recipe.getIngridients());
gen.writeEndObject();
}
}
Applying serializer:
#JsonSerialize(using = RecipeSerializer.class)
public class Recipe {
// model entity
}
JSON response body of EvaUser from controller (previous one was StackOverflowError):
{
"myRecipes": [
{
"name": "soup",
"author": "user1",
"ingridients": "carrots, tomatos"
},
{
"name": "steak",
"author": "user1",
"ingridients": "meat, salt"
}
],
"favoriteRecipes": [
{
"name": "soup",
"author": "user1",
"ingridients": "carrots, tomatos"
},
{
"name": "steak",
"author": "user1",
"ingridients": "meat, salt"
}
],
"username": "user1"
}
How can I parse this response without having to create a separate response class for each entity.
{
"data": {
"id": 100,
"first_name": "Michael",
"last_name": "Blankenship"
}
}
I would like to have a generic class that can reference the data object and then just specify what type of class that should be used to parse the response
Something like this:
#Get
Call<User> getUser();
#Get
Call<Status> getStatus();
Without having to have multiple response classes for each type
public class UserResponse {
User data;
}
public class User {
String first_name;
String last_name;
}
public class StatusResponse {
Status data;
}
Workaround for this would create a generic class something like this
public class BaseResponseWrapper <T> {
#SerializedName("data")
private T data;
public BaseResponseWrapper(){
super();
}
public T getData() {
return data;
}
}
I'm using Retrofit to send picture request and receive this Json
{"response":{
"face":{
"value":"true",
"confidence":55
},
"gender":{
"value":"male",
"confidence":73
},
...
}}
and I'm receiving it with Retrofit....
RestAdapter adapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(END_POINT)
.build();
Mylistenerr listener = adapter.create(Mylistenerr.class);
File photo = new File(picturePath);
TypedFile image = new TypedFile("multipart/image/jpg", photo);
listener.setUserImage(
image,
new Callback<respostring>() {
#Override
public void success(respostring rp, Response arg1) {}
#Override
public void failure(RetrofitError arg0) {
pd.hide();
pd.dismiss();
Log.d("ERROR:", arg0.getLocalizedMessage());
}
});
}
private static class respostring {
private Content face;
private Content gender;
respostring() {}
}
class Content
{
private int confidence;
private String value;
Content(){}
public int getconf(){
return this.confidence;
}
public String getvalue(){
return this.value;
}
}
My interface
public interface Mylistenerr {
#Multipart
#POST("/public/test")
void setUserImage(
#Part("image") TypedFile file,
Callback<respostring> response);
}
but there is retrofit error. Is there something I miss here?
I'd recommend you using Gson for json deserialization instead since retrofit supports it very well. Then you can just create classes like this:
Your face class:
public class Face implements Serializable {
#SerializedName("value")
public boolean value;
#SerializedName("confidence")
public int confidence;
}
Your gender class:
public class Gender implements Serializable {
#SerializedName("value")
public String value;
#SerializedName("confidence")
public int confidence;
}
your response class:
public class YourResponseType implements Serializable {
#SerializedName("face")
public Face face;
#SerializedName("gender")
public Gender gender;
}
Then you can actually make retrofit doing the rest for you:
listener.setUserImage(image, new Callback<YourResonseType>() {...});
Hope that helps!