Imagine I have a service to retrieve some authors (id, firstname, lastname, birthday, nationality) and a second service to retrieve an author's book (title, summary, number of pages).
I would like my network call to return me a list of authors with their books.
To illustrate, my services would return me AuthorOResponse and BookResponse and I would like to emit an Observable of AuthorObject.
public class AuthorResponse {
private String id;
private String firstname;
private String lastname;
private Date birthday;
private String nationality;
}
public class BookResponse {
private String title;
private String summary;
private int pageNumber;
}
public class AuthorObject {
private String id;
private String firstname;
private String lastname;
private Date birthday;
private String nationality;
private List<BookObject> books
}
My services would be something like
Observable<List<AuthorResponse>> getAuthors()
Observable<List<BookResponse>> getAuthorBooks(String authorId)
I would like to use them to emit an Observable but cannot figure out how to do it since each of my calls to getAuthorBooks need an author.
I came up with something looking like this but I have an issue since my concatMap make me "loose" my author.
myServices.getAuthors()
.map(author -> createAuthor())
.concatMap(author -> mWebService.getAuthorBooks(author.getId())
.flatMapIterable(data -> data.items)
.map(book -> addBook())
Does someone know how I could do this ?
You could use special flatMap overload designed for such cases:
myServices.getAuthors()
.map(author -> createAuthor())
.flatMap(author -> mWebService.getAuthorBooks(author.getId()),
(author, data) -> {
author.setBooks(data.items));
return Observable.just(author);
})
.toList() //here is your Observable<List<Author>>
Maybe something like this:
getAuthors.map(new Func1<List<AuthorResponse>, AuthorObject>() {
#Override
public AuthorObject call(List<AuthorResponse> authorResponses) {
return createAuthor();
}
}).flatMap(new Func1<AuthorObject, Observable<AuthorObject>>() {
#Override
public Observable<AuthorObject> call(final AuthorObject authorObject) {
return getAuthorBooks(authorObject.id)
.map(new Func1<List<BookResponse>, AuthorObject>() {
#Override
public AuthorObject call(List<BookResponse> bookResponses) {
authorObject.books = parseBooks(bookResponses);
return authorObject;
}
});
}
});
Query the Authors, map each AuthorResponse to AuthorObject, then using flatMap query books for each Author, and map it in order to add parsed books to the generated earlier AuthorObject.
at the end you'll get an Observable that emits AuthorObject with their books list populated.
Related
I have ServerTime class:
public class ServerTime {
private String time;
public ServerTime(String time) {
this.time = time;
}
public String getTime() {
return time;
}
}
And ServerNetwork class:
public class ServerNetwork {
private String id;
private String name;
public ServerNetwork(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName(){
return name;
}
}
Now what I wanted to do is process this sequentially since I have to supply timestamp to the second api call. So far I have this but I'm unsure how to process the first observable result, like converting it to a timestamp and then using it for the second observable api request.
Observable<ServerNetwork> serverNetworkObservable = apiInterface
.getTime()
.flatMap(time -> apiInterface.getNetworks(time, anothervalue))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
How do I go about starting these observables to actually call the api? I'm new to RxJava.
Try:
apiInterface.getTime()
// specify thread for performing network call.
.subscribeOn(Schedulers.newThread())
// use emissions from 1st Observable in 2nd Observable.
.flatMap(serverTime -> {
String anotherValue = "";
return apiInterface.getNetworks(serverTime.getTime(), anotherValue);
})
// switch to computation thread to transform emissions (optional)
.observeOn(Schedulers.computation())
// transform emissions from Observable<ServerNetwork> to Observable<String> (corresponding network ids).
.map(ServerNetwork::getId)
// switch to UI thread to consume the transformed emissions.
.observeOn(AndroidSchedulers.mainThread());
.subscribe(serverNetworkId -> {
// update views, etc
textView.setText(serverNetworkId);
});
This is the inferred interface:
public interface ApiInterface {
#(...)
Observable<ServerTime> getTime();
#(...)
Observable<ServerNetwork> getNetworks(#Body String timeStamp, #Body String anotherValue);
}
Note: Since you did not include code for your ApiInterface.class file I have tried to infer what that file may contain. I have also included some comments to explain what is going on under the hood to add some clarity. I included an optional operator .map() and used multiple observeOn, they are completely optional.
I created an object to send some data to firebase. As an example, I use firebase user example:
public class User {
public String username;
public String email;
public User() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
public User(String username, String email) {
this.username = username;
this.email = email;
}
}
I want to encode property names that are sent to firebase. Currently keys are sent using variable names. I want to encode keys something like Useraname and Email, like Gson is doing. I don't want to change variable names.
#SerializateName("Username")
public String username;
#SerializateName("Username")
public String email;
I used #SerializateName(), but is not working. Same with #PropertyName that is used by Firebse, is not working. What I can use in order to serializare custom keys?
Update 1
public class Pojo {
#PropertyName("Guid")
public String guid;
#PropertyName("Name")
public String name;
public String getPojoGuid() {
return guid;
}
public void setPojoGuid(String guid) {
this.guid = guid;
}
}
As you can see in the image, it saves keys based on variable names. I changed property name from annotation for one field and when i save it, it ignores it, but when i change variable name, it save as new entry with key for that new varialbe name.
In this documentation is a method toMap(). If i do like that, is working (is not convenient for me), but is not working with #PropertyName.
Update 2
If i mark getters and setters with #Exclude and class with #IgnoreExtraProperties is working. I don't have to use toMap() method example from documetation. Is using specified name from #PropertyName. Not a good thing in my opinion, create confuses.
The Firebase SDK uses the annotation it finds for the property whenever it gets or sets its value. That means you need to consider how Firebase gets/sets the value, and annotate each place it looks.
Since you're declaring a getter method, Firebase will use that to get the value of the property. It will use the field for setting the value. So the annotation needs to be on both:
public class Pojo {
#PropertyName("Guid")
public String guid;
#PropertyName("Name")
public String name;
#PropertyName("Guid")
public String getPojoGuid() {
return guid;
}
#PropertyName("Guid")
public void setPojoGuid(String guid) {
this.guid = guid;
}
}
If you'd have getters and setters, the annotation would need to be on those, but not on the fields anymore:
public class Pojo {
private String guid;
private String name;
#PropertyName("Guid")
public String getPojoGuid() {
return guid;
}
#PropertyName("Guid")
public void setPojoGuid(String value) {
guid = value;
}
#PropertyName("Name")
public void setPojoGuid(String guid) {
this.guid = guid;
}
#PropertyName("Name")
public void setPojoGuid(String value) {
name = value;
}
}
What you are looking for is the feature of SDK Version 9.2 in which you can now use a new #PropertyName attribute to specify the name to use when serializing a field from a Java model class to the database. This replaces the #JsonProperty attribute.
#PropertyName("Username")
public String username;
#PropertyName("Email")
public String email;
See also this post in which Frank van Puffelen explains very clearly this concept.
#PropertyName :
Marks a field to be renamed when serialized. link
you have to use #PropertyName with public fields and no need for getters/setters
I'm trying to use RxJava to combine the responses from two API calls. The scenario is the following: The first API call returns a list of User objects. Each User object contain a postcode, but they do not contain a city name. To get the city name a second API is called, with the parameter being the postcode.
Ideally I want to modify the original list of User objects, and add the city name after it has been fetched in the second API call, but I'm stuck at figuring out how to do it.
This seems like a pretty straightforward observable:
Dummy API for users:
public static List<String> getUsers() {
return new ArrayList<String>() {
/**
*
*/
private static final long serialVersionUID = 1L;
{
add("Anakin");
add("Darth");
}
};
}
Dummy API for city:
public static String getCity(String name) {
// dummy ..
return "Mos Espa";
}
Merging together:
public static void main(String[] args) throws InterruptedException {
Observable.from(getUsers()).flatMap(
// we have the user
user -> Observable.just(getCity(user)).map(
// we have the city
city -> user + " from " + city)) // do stuff with user + city
.subscribe(result -> System.out.println(result));
}
Here is my understanding of your question. You have 2 API calls...
public Observable<List<User>> getUsers();
public Observable<String> getCityName(String postalCode);
And you want to set each user's city name based on the call to getCityName. You can convert the List into an observable stream by flatMapping it into an Observable from an Iterable. Here is one way to do that.
Observable<User> usersWithCity = getUsers()
.flatMap(new Func1<List<User>, Observable<User>>() {
#Override
public Observable<User> call(List<User> users) {
return Observable.from(users);
}})
.flatMap(new Func1<User, Observable<User>>() {
#Override
public Observable<User> call(User user) {
return getCityName(user.getPostalCode())
.doOnNext(new Action1<String>() {
#Override
public void call(String t) {
user.setCityName(t);
}
})
.map(new Func1<String, User>() {
#Override
public User call(String cityName) {
return user;
}
});
}
});
Note that this makes the assumption that there will only ever be one cityName for each postalCode.
I'm using Android Studio 1.5, and using the following code straight from the documentation itself
public class User {
private int birthYear;
private String fullName;
public User() {
}
public User(String fullName, int birthYear) {
this.fullName = fullName;
this.birthYear = birthYear;
}
public long getBirthYear() {
return birthYear;
}
public String getFullName() {
return fullName;
}
}
Firebase code
Firebase ref = new Firebase("https://docs-examples.firebaseio.com/android/saving-data/fireblog");
Firebase alanRef = ref.child("users").child("alanisawesome");
User alan = new User("Alan Turing", 1912);
alanRef.setValue(alan);
It throws an error cannot resolve symbol setValue. When I try the autocomplete feature, there's no suggestion for setValue(); however if used on the RHS of the assignment operation like in the code below, it works. Can someone help me out here?
Firebase abc = ref.setValue(); //seems to work
I have not seen your entire code but I am assuming that you tried using setValue outside a method. The setValue method can only be invoked inside a method.
I have a JSON string that contains a nested json like
{
"name": "name",
...
...
"profile": {
"id": 987,
"first_name": "first name"
...
...
}
}
I'm trying to map this JSON into Realm by using the method realm.createObjectFromJson(Class clazz, String string) and the problem is that the nested JSON is not mapped, the resulting RealmObject instance that corresponds to the "profile" has 0's and null's for all the fields. I used realm.beginTransaction() before the create operation, and realm.commitTransaction() after.
I'm using 'io.realm:realm-android:0.80.1' for my Android project.
Can you please tell me what am I doing wrong?
Thanks.
EDIT
These are my model classes. Simple RealmObjects linked together
public class SomeClass extends RealmObject {
private String name;
private Profile profile;
public Profile getProfile() {
return profile;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name= name;
}
}
public class Profile extends RealmObject {
private String firstName;
private String lastName;
private String birthdate;
private boolean newsLetter;
private boolean push;
private int userId;
private Date lastUpdate;
private RealmList<RealmAddress> addresses;
private RealmList<RealmGender> genders;
}
the profile class contains only getters and setters, and it contains other Strings and ints, which I deleted for the sake of simplicity.
Your JSON names doesn't match your child object field names which is why you don't see any data. Your profile name matches the field in SomeClass, which means the object gets created (with default values), but as none of the fields match in Profile, none of them are set.
firstName != first_name
userId != id
If you want to have separate names in your JSON and the Java models you should use something like GSON (http://realm.io/docs/java/#gson) as that is not yet supported by Realm directly.
use this :
public class Profile extends RealmObject {
private String first_name;
private int id;
...
}
check that you have the same names in JSON and your class model