Using Retrofit and GreenDao with nested json objects - android

I want to combine Retrofit and GreenDao but I have a problem with nested Json-Objects. My nested fields remain empty.
This is the Json DataStructure
[
{
"id": 1,
"street": "Streetname",
"zipcode": 12345,
"city": "MyCity",
"phone_number": "+123456789",
"position": "12.0000, 9.0000",
"company": {
"title": "CompanyName",
"group": {
"title": "GroupName"
}
}
}
]
My DaoGenerator looks like this
Entity customItem = schema.addEntity("CustomItems");
customItem.addIdProperty();
customItem.addStringProperty("street");
customItem.addIntProperty("zipcode");
customItem.addStringProperty("city");
customItem.addStringProperty("phone_number");
customItem.addStringProperty("position");
Entity company = schema.addEntity("Company");
company.addIdProperty();
company.addStringProperty("title");
Entity group = schema.addEntity("Group");
group.addIdProperty();
group.addStringProperty("title");
Property companyPropId = customItem.addLongProperty("companyId").notNull().getProperty();
customItem.addToOne(company, companyPropId);
Property groupPropId = company.addLongProperty("groupId").notNull().getProperty();
company.addToOne(group, groupPropId);
My problem is that customItem.getCompany() returns null but the values "id" to "position" are fine. I'm not sure what the problem is as my CustomItem class contains the member
private Company company;
and the setter for the company and I can't see any typo.
public void setCompany(Company company) {
if (company == null) {
throw new DaoException("To-one property 'companyId' has not-null constraint; cannot set to-one to null");
}
synchronized (this) {
this.company = company;
companyId = company.getId();
company__resolvedKey = companyId;
}
}

I got it running but I had multiple problems.
1) When I wanted to persist the CustomItem, Company, and Group I had the problem that the getters getCompany() and getGroup() returned null because they don't return the member directly but fetch it from the DB. Therefore I added a getter to the generated CustomItem entity class that simply returns the company member. Now I was able to insert company to the db. The getter looks like this:
// KEEP METHODS - put your custom methods here
public Company getCompanyLocal() {
return company;
}
// KEEP METHODS END
The same works for Company and Group, too. But there was another issue...
2) The second problem was the entity 'Group' as 'group' is a reserved SQL keyword. I see one solution and a bad workaround to this problem:
The good one is to change you json data from 'group' to i.e. 'business_group' and according to that change your DAOs. Done.
The bad workaround if you are in the same situation like me where you can't change the json you could do the following. I don't persist the group at all but can access it via the company. It somehow appears there. Therefore I added a getter to my Company class like the getter of CustomItem above. It works but you should avoid this. As you can't query your DB for group or load group from db.

To solve your second problem, add this code to your DAO generator:
beacon.addStringProperty("business_group"); //Daogenerator
And add this code into your network manager:
//add this into your network manager
FieldNamingStrategy strategy = new FieldNamingStrategy() {
#Override
public String translateName(Field field) {
if(field.getName().equalsIgnoreCase("business_group")) {
return "group";
} else {
return field.getName();
}
}
};
And set this property to your Gson:
//add this in your Gson
.setFieldNamingStrategy(strategy)
hopefully it helps!!

Related

Best Way to Create Room Database Object

I am New to Room Persistence Library in Android. I have looked into a lot of tutorials related to Room. But something is unclear about it.
I can relate my questions to the below response. If we use #Embedded in "Info", Is it possible to declare info POJO class as separate entity? because inside info I have another object called "preferences". How can I embed that to my Room Database ?
Next is How can I use Awb ArrayList in below sample for Type converters? Note that inside Awb ArrayList has one more "terms" array inside.
I would appreciate if some can show and explain to me the best way to Create Room from the below sample data. I know so many tutorials are there for simple JSON structure, but I didn't find any examples with nested JsonObjects.
{
"hashCode": 10461,
"jobs": [
{
"info": {
"jobDesc": "aaaaa",
"signatureRequired": "true”
"preferences": {
"podRequired": 0,
"eligableForCcod": false,
"signatureRequired": 1
}
},
"awb": [
{
"terms": [
{
"termValue": "INR1500.000",
"termType": "CASH_AMOUNT"
},
{
"termValue": "CTLDk",
"termType": "CX_EN"
}
],
"packagesCount": 1,
"accountId": "AE _MP",
"awb": "1234567878440"
}
]
}
]
}
I don't know if this is the best possible approach, but normally when I have complex objects in Room I define a converter class containing two TypeConverters. The first one converts from the structured object (your POJO class) to a String with GSON library, something like:
#TypeConverter
public String fromPojoToString(MyPojoClass info) {
if (info == null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<MyPojoClass>() {
}.getType();
return gson.toJson(info, type);
}
The second converter converts from the string (stored inside Room) to the structured object (the POJO class). Something like:
#TypeConverter
public MyPojoClass fromStringtoPojo(String object) {
if (object == null) {
return (null);
}
Gson gson = new Gson();
Type type = new TypeToken<MyPojoClass>() {
}.getType();
return gson.fromJson(info, type);
}
This way you'll be able to serialize/deserialize your POJO class without writing a lot of boilerplate. Obviously, you have to annotate your AppDatabase class (the one that extends RoomDatabase) with your Converter using #TypeConverters annotation.
There is a good article about this approach here https://medium.com/#toddcookevt/android-room-storing-lists-of-objects-766cca57e3f9.
Hope that this helps, best luck!
Explore these link to easy understand room persistance database
https://www.youtube.com/watch?v=KAHAQunQkDE
https://developer.android.com/topic/libraries/architecture/adding-components#room
https://developer.android.com/training/data-storage/room/

JsonArray as empty string parsing issue with retrofit

I have a json in which 1 key is coming as jsonArray if it has data otherwise it is coming as empty string. It is giving error while parsing in gson with retrofit.
"section": "Technology",
"subsection": "",
"title": "Depiction of Amazon Stirs a Debate About Work Culture",
"abstract": "Details of working conditions at Amazon led to a response from employees, relatives and friends.",
"url": "http://www.nytimes.com/2015/08/19/technology/amazon-workplace-reactions-comments.html",
"byline": "By THE NEW YORK TIMES",
"item_type": "Article",
"updated_date": "2015-08-18T07:35:33-5:00",
"created_date": "2015-08-18T07:35:35-5:00",
"published_date": "2015-08-19T04:00:00-5:00",
"material_type_facet": "News",
"kicker": "",
"des_facet": [
"Workplace Environment"
],
"org_facet": [
"Amazon.com Inc"
],
"per_facet": "",
"geo_facet": "",
des_facet , org_facet, per_facet, geo_facet are jsonArray but you can see that 2 are not having data so coming as empty string.
How to handle this scenario with retrofit +gson.
Json format can't be changed here at server.
is there any way I can achieve it in android?
Ok so there are two option you can solve this
Option 1:
JSON which I used as a example
"des_facet": [
"Workplace Environment"
],
"org_facet": [
"Amazon.com Inc"
],
"per_facet": ["Akshay"],
"geo_facet": ""
In your model class convert those variable to Object type
#Expose
#SerializedName("geo_facet")
private Object geo_facet;
#Expose
#SerializedName("per_facet")
private Object per_facet;
then where you want to set data do the following
if (model != null)
{
if (model.getGeo_facet() != null || model.getGeo_facet() != "")
{
Object arr = model.getGeo_facet();
}
if (model.getPer_facet() !=null || model.getPer_facet()!= "")
{
Object arr = model.getPer_facet();
if (arr!=null && arr.toString().length()>0)
{
arr = arr.toString();
Log.d("akshay","arr= "+arr);
//Do your Stuff or Set data
}
}
}
This is the output= 08-11 16:51:29.830 17951-17951/com.android.example
D/akshay: arr= [Akshay]
Option 2:
Follow this which is a little bit complex
Option 3:
Write own custom Parsing like this and Handle your response accordingly
a json can have a single structure. From the code it is clear that the key is given with 2 types of data
Ideally, it should not give "" when no items. It should give null
Please change the data
If no items,
"des_facet"=null // this is the change on server side. No need to change it on app side
If it has items
"des_facet"=[
"Workplace Environment"
]
instead of
If no items,
"des_facet"=""
If it has items
"des_facet"=[
"Workplace Environment"
]
You can use AutoValue with gson plugin and mark the field as nullable which will notify the Autovalue to make this field optional.
AZs an example this is how you do it:
#AutoValue
public abstract class NewsResponse{
public static TypeAdapter<NewsResponse> typeAdapter(Gson gson){
return new AutoValue_NewsResponse.GsonTypeAdapter(gson);
}
#SerializedName("api_status")
public abstract String apiStatus();
#SerializedName("api_text")
public abstract String success();
#Nullable
#SerializedName("errors")
public abstract ErrorDetails errorDetails();
#SerializedName("news")
public abstract List<NewsDetails> newsDetails();
}
you must import both of them see more info about importing at: AutoValue and AutoValue Gson Plugin

Add items to the list in firebase database and retrieving it on ArrayList in Android

I need to store the list of values in following the database structure.Also how to get the list of particular post's("first") values.
"userviews" :{
"first":{
1:
"userEmail":"abc#gmail.com",
2:
"userEmail":"xyz#gmail.com"
},
"second":{
1:
"userEmail":"abc#gmail.com",
2:
"userEmail":"xyz#gmail.com"
}
}
I am developing similar to blog application.Here "userviews" is the root of the database." first" and "second" are post titles.Suppose if the user has seen the post "first", their mail has been sent to "first" list in the Firebase database.
I tried with this following solution but I didn't get proper format
String userEmail = "abc#gmail.com";
ViewedUsers viewedUsers = new ViewedUsers();
viewedUsers.setUserEmail(userEmail);
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("userviews").child("first").setValue(viewedUsers);
if I doing like this, I am getting following format.
"userviews" :{
"first":{
"userEmail":"abc#gmail.com"
}
}
Model Class : ViewedUser
public class ViewedUsers {
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
String userEmail;
}
UPDATE
"userviews" :{
"first":{
0:"abc#gmail.com",
1:"xyz#gmail.com"
},
"second":{
0:"abc#gmail.com",
1:"xyz#gmail.com"
}
}
OLD
As per my knowledge and experience, Firebase is totally depends on key value parameters if I am not wrong. Please let me know if I am wrong somewhere.
If you don't want to have keys in your database then what can you do is convert your values in key. Something like this:
"userviews" :{
"first":{
"abc#gmail.com":0
"xyz#gmail.com":0
},
"second":{
"abc#gmail.com":0
"xyz#gmail.com":0
}
}
This way your values become your keys and you can easily retrieve it as you retrieve your values.
You can also save it as array. As suggested by #Simon B
For fetching particular node you may refer this link and this. It is just simple. If you still found any issue please let me know.

How to read the object inside a JSON array

I have JSON in this format.
I m trying to create serialization class to store the value.
How to read the "personaldata" field?
I am making a separate class PersonalData to read it.
And in my main serialization class I am reading it as
List<PersonalData>personalData
Is it the right way to do it?
If so, how will I fetch the personal data values?
{
"result": [
{
"name": 0,
"age": 1,
"class": 0,
// More data here
"personalData": {
"isMarried": true,
"isEligible": false,
"Indian": true
}
}
]
}
If you are using some parser like GSON or LoganSquare you can use their annotations and it will be really easy to parse JSON directly to your model. Otherwise if you are using native JSON API
You can use something like this
JSONArray arr=new JSONArray(response);
JSONObject personalData=arr.getJSONObject("personalData");
I am making a separate class PersonalData to read it..
Okay, then that is how you access it. By getting that object from some parent Results object.
For example, given some implementation of the below classes, once you deserialize the JSON, you use Results.getResults().get(0).getPersonalData();
public class Results {
ArrayList<ResultData> result;
// TODO: write getResults()
}
public class ResultData {
int name, age, class;
// Some more data
PersonalData personalData;
// TODO: write getPersonalData()
}
public class PersonalData {
boolean isMarried, isEligible, Indian;
}

Save complex objects and their sons entities ORM Lite

I'm using ORM Lite on a project , I decided to use the facility to make the persistence part of the Web service once and can reuse it on Android.
But I am suffering a lot because possou complex objects that have multiple ForeignCollectionField and Foreign object , and at the hour of perssistir temenda these data is a headache, because I have to enter one by one of their children , I think the idea of ​​an ORM is make life easier , ie you have to persist the object and father and all the rest is done behind the scenes ...
Well, it is now too late to give up lite ORM , I wonder if there is a way to do what sitei above ..
I found a piece of code here
tried to implement but it seems not work , just keeps saving the parent object .
follows the function I'm trying to use , but do not know whether imports are correct because the code I found in the link above did not have this data
public int create(Object entity, Context context) throws IllegalArgumentException, IllegalAccessException, SQLException, SQLException {
try{
if (entity!=null){
// Class type of entity used for reflection
Class clazz = entity.getClass();
// Search declared fields and save child entities before saving parent.
for(Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
// Inspect annotations
Annotation[] annotations = field.getDeclaredAnnotations();
try{
for(Annotation annotation : annotations) {
// Only consider fields with the DatabaseField annotation
if(annotation instanceof DatabaseField) {
// Check for foreign attribute
DatabaseField databaseField = (DatabaseField)annotation;
if(databaseField.foreign()) {
// Check for instance of Entity
Object object = field.get(entity);
Dao gDao = getDatabase(context).getDao(object.getClass());
gDao.create(object);
}
}else if (annotation instanceof ForeignCollectionField){
Object object = field.get(entity);
for(Object obj : new ArrayList<Object>((Collection<?>)object)){
Class c = obj.getClass();
Dao gDao = getDatabase(context).getDao(obj.getClass());
gDao.create(obj);
}
}
}
}catch (NullPointerException e){
e.printStackTrace();
}
}
// Retrieve the common DAO for the entity class
Dao dao = getDatabase(context).getDao(entity.getClass());
// Persist the entity to the database
return dao.create(entity);
}else
return 0;
}finally {
if (database != null) {
OpenHelperManager.releaseHelper();
database = null;
}
}
}
Leveraging the same post, also need a colução to delete cascade, imagine a situation where I have the following tables:
Company > Category> person> contact> Phone and email
Deleting and now I do as described in the documentation:
public int deleteCascade(Prefeitura prefeitura, Context context){
try{
Dao<Prefeitura, Integer> dao = getDatabase(context).getDao(Prefeitura.class);
DeleteBuilder db = dao.deleteBuilder();
db.where().eq("prefeitura_id", prefeitura.getId());
dao.delete(db.prepare());
// then call the super to delete the city
return dao.delete(prefeitura);
}catch (SQLException e){
e.printStackTrace();
}
return 0;
}
But the objects that are not directly linked the company would still be in the database, how could I do?
But without hacks, I want a clean code ...
I know ORM Lite really is lite, but one that saves the children create and delete cascade is essential for any ORM, hopefully for the next versions it is implemented, it is regrettable not have these features, for simple projects is very good, but in a complex project because a lot of headaches, I'm feeling on the skin.
Any help is welcome!

Categories

Resources