【问题标题】:How to change list of objects using RxJava如何使用 RxJava 更改对象列表
【发布时间】:2016-02-24 17:47:56
【问题描述】:

我正在尝试使用 StackOverFlow API。我想要带有名称、计数和描述标签列表。问题是描述在不同的api函数中返回:

https://api.stackexchange.com/2.2/tags?page=1&pagesize=3&order=desc&sort=popular&site=stackoverflow

只给了我三个所需字段中的两个:(点击链接试试):

  • 1)名称
  • 2)计数

https://api.stackexchange.com/2.2/tags/javascript%3Bjava%3Bc%23/wikis?site=stackoverflow 获取标签名称并给我:

  • 3)摘录(描述)

我建立了一个 Retrofit 界面:

 class Item {
    @SerializedName("name")
    public String name;
    @SerializedName("count")
    public String count;
    @SerializedName("excerpt")
    public String excerpt;
}

class Items {
    @SerializedName("items")
    public Item[] items;
}

@GET("/2.2/tags")
Observable<Items> getItems(@Query("page") String page,
                          @Query("pagesize") String pagesize,
                          @Query("order") String order,
                          @Query("sort") String sort,
                          @Query("site") String site);


@GET("/2.2/tags/{tagnames}/wikis")
Observable<Items> getDescription(@Path("tagnames") String tagnames,
                                        @Query("site") String site);

所以,首先,我们应该执行 getItems,然后获取所有名称并执行 getDescription func,最后用新字段填充我们的 Items。 所以我想收集所有 3 个(名称、计数和摘录在一个 observable 中)。

到目前为止,这是我的观察者:

App.getServerAPI()
            .getItems("page", "pagesize", "order","sort", "site") //just example strings
            .observeOn(AndroidSchedulers.mainThread());

我应该怎么做?我应该在这两个函数中使用 Observable 吗?

更新: 听从你的建议,我写了这么一篇:

public Observable<ServerAPI.Tags> call() {
                        return App.getServerAPI()
                                .getItems("page", "pagesize", "order", "sort", "site")
                                .map(new Func1<ServerAPI.Tags, ServerAPI.Tags>() {
                                    @Override
                                    public ServerAPI.Tags call(ServerAPI.Tags tags) {
                                        String tagsStr = "";
                                        for (ServerAPI.Tag tag: tags.items){
                                            tagsStr +=tag.name+";";
                                        }
                                        ServerAPI.Excerts excerts = App.getServerAPI().
                                                getExcerts(tagsStr, "site");
                                        for (ServerAPI.Excert excertItem : excerts.items){
                                            for (ServerAPI.Tag tagItem: tags.items){
                                                if (excertItem.tag_name.equals(tagItem.name)){
                                                    tagItem.excerpt = excertItem.excerpt;
                                                }
                                            }
                                        }
                                        return tags;
                                    }
                                })
                                .delay(1, TimeUnit.SECONDS)
                                .observeOn(AndroidSchedulers.mainThread());

                    }

看起来很丑!

【问题讨论】:

  • 你必须创建一个 Observable,用 2 个 observables 制作一个 .zip,完成后你有 2 个结果,并用 2 个结果创建具有名称、计数和摘录的项目

标签: android retrofit rx-java rx-android


【解决方案1】:

我会这样做:

class Tag{
    public String name;
    public String count;
}

class TagList {
    public List<Tag> tags;
}

class Excert {
    public String excert;
}

class Item {
    public final String name;
    public final String count;
    public final String excert;

    public Item(String name, String count, String excert) {
        this.name = name;
        this.count = count;
        this.excert = excert;
    }
}

Observable<TagList> getItems() {}

Observable<Excert> getExcert(String tagName) {}


public void testFlatMap() {
    getItems()
             //iterate over each Tag
            .flatMap(tagList -> Observable.from(tagList.tags))
             //call server for excert
            .flatMap(tag -> getExcert(tag.name),
                    new Func2<Tag, Excert, Item>() {
                        @Override
                        public Item call(Tag tag, Excert excert) {
                            //compose final data structure
                            return new Item(tag.name, tag.count, excert.excert);
                        }
                    })
            //item here
            .subscribe(item -> {

            });
}

更新

这里是调用 Api 以获取标签列表的示例。

class Tag {
    public String name;
    public String count;
}

class TagList {
    public List<Tag> tags;
}

class Excert {
    public String excert;
    public String tagName;
}

class ExcertResponse {
    public List<Excert> items;
}

class Item {
    public final String name;
    public final String count;
    public final String excert;

    public Item(String name, String count, String excert) {
        this.name = name;
        this.count = count;
        this.excert = excert;
    }
}

Observable<TagList> getItems() {
    return Observable.empty();
}

Observable<ExcertResponse> getExcert(String tagName) {
    return Observable.empty();
}

private String constructTagsList(Map<String, Tag> stringTagMap) {
    StringBuilder builder = new StringBuilder();
    for (String tagName : stringTagMap.keySet()) {
        if (builder.length() != 0) {
            builder.append(";");
        }
        builder.append(tagName);
    }
    return builder.toString();
}

public void testFlatMap() {
    getItems()
            //create map in order to easily find tag
            .map(tagList -> {
                Map<String, Tag> map = new HashMap<String, Tag>(tagList.tags.size());
                for (Tag tag : tagList.tags) {
                    map.put(tag.name, tag);
                }
                return map;
            })
            .flatMap(stringTagMap -> {
                        String tagsList = constructTagsList(stringTagMap);
                        return getExcert(tagsList);
                    },
                    new Func2<Map<String, Tag>, ExcertResponse, List<Item>>() {
                        @Override
                        public List<Item> call(Map<String, Tag> stringTagMap, ExcertResponse response) {
                            List<Item> items = new ArrayList<Item>(stringTagMap.size());
                            for (Excert excert : response.items) {
                                Tag tag = stringTagMap.get(excert.tagName);
                                Item item = new Item(tag.name, tag.count, excert.excert);
                                items.add(item);
                            }
                            return items;
                        }
                    })
            //List of items here
            .subscribe(item -> {

            });
}

【讨论】:

  • 我觉得这个方法很慢而且没有使用 Stackoverflow api 的所有功能:/2.2/tags/javascript;java;c#/wikis?site=stackoverflow -- 我们最多可以放 20 个标签在一起
  • @FixedGear 我试图展示如何使用rxJava 链接多个 api 调用。如果您不想遍历每个标签,请不要这样做。而是应用 map/flatMap 来创建任何其他有效的输入参数。或者直接在getExcert 调用之前进行。
  • 你能通过扩展你的答案来描述这种方法吗?
【解决方案2】:

您可以将getDescription 方法更改为:

Observable<Items> getDescription(@Path("tagnames") String tagnames,
                                        @Query("site") String site);

DescriptionResponse getDescription(@Path("tagnames") String tagnames,
                                        @Query("site") String site);

其中DescriptionResponse代表响应对象:

public class DescriptionResponse {
    List<DescriptionItem> items;
}

然后获取项目并使用map 运算符获取描述并将它们映射到您的项目:

api.getItems("", "", "", "", "").map(items -> {
    String tags = "";//iterate over items and grab all tags
    DescriptionResponse description = api.getDescription(tags, "");
    //iterate over items and descriptions in response and set appropriate description
    return items;
}).subscribe(items -> {
    //here is your items with descriptions
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多