【问题标题】:Group messages from json file by user id按用户 ID 对来自 json 文件的消息进行分组
【发布时间】:2015-07-16 21:45:33
【问题描述】:

我能够读取包含消息列表及其相应作者的 JSON 文件。单个消息的结构如下:

JSON

{
"created_at": "Thu Apr 30 10:47:49 +0000 2015",
"id": 593728455901990900,
"user": {
    "id": 12,
    "name": "GiGi",
    "user_date": "Thu May 17 10:47:49 +0000 2010"
}

}

Author 和 Message 类 (POJO) 包含我们要解析的字段以及以字符串形式显示字段的函数 toString()

作者

public class Author {

    @SerializedName("id")
    private static long id;

    @SerializedName("created_at")
    private static String user_date;

    private static String name;

    public  String toString() {
        return name + "\n" + id + "\n" + user_date;         
    }
    ....Getters & Setters....
}

留言

public class Message {

    @SerializedName("user")
    Author author;   

    @SerializedName("created_at")
    String date;

    long id;

    public String toString() {
        return id + "\n" + date;        
    }

}

Author 是 Message 类的成员类,我需要显示按用户分组的消息,但我似乎没有找到一种优雅的方式。

更新

一种解决方案是创建一个 Map,其中 Author 的属性之一为 KEY(在此代码示例中为作者的 id),相应的消息列表为 VALUE。

Map<Long, List<Message>> map = new HashMap<Long, List<Message>>();
for (Message msg : listOfMessages) {      
    long key = msg.getAuthor().getId();

    if (map.get(key) == null) {
        map.put(key, new ArrayList<Message>());
    }
    map.get(key).add(msg);
}

此解决方案可能引发的问题:

  • 如何显示作者id AND姓名及其对应的消息列表?
  • 我不太确定这个解决方案的优雅程度,因为 Message 类包含 Author 类,并且在 MAP 值中“携带”了许多不必要的信息。

解决方案

在 Author 类中添加 equalshashCode 方法后,我们可以将 Author 实例映射为 Key。

@Override
public int hashCode() {
    int hash = 7;
    hash = 23 * hash + (int) (this.id ^ (this.id >>> 32));
    return hash;
}

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final Author author = (Author) obj;
    if (this.id != author.id) {
        return false;
    }
    return true;
}

在主类中,我们可以遍历消息列表并正确填充地图:

Map<Author, List<Message>> map = new HashMap<Author, List<Message>>();
for (Message message : listOfMessages) {
    List<Message> userMessages = map.get(message.getAuthor());
    if (userMessages == null) {
        userMessages = new ArrayList<Message>();
        map.put(message.getAuthor(), userMessages);
    }
    userMessages.add(message);
}
for (Entry<Author, List<Message>> entry : map.entrySet()) {
    System.out.println(entry.getKey().getId());
    for (Message message : entry.getValue()) {
        System.out.println("\t" + message.getDate());
    }
}

输出

3037057186
    Thu Apr 30 10:47:52 +0000 2015
1598532858
    Thu Apr 30 10:47:51 +0000 2015
67267979
    Thu Apr 30 11:47:49 +0000 2015
    Thu Apr 30 10:47:49 +0000 2015
    Thu Apr 30 10:47:49 +0000 2015
    Thu Apr 30 10:47:49 +0000 2015

【问题讨论】:

  • display the messages grouped by user?用户名?

标签: java json


【解决方案1】:

首先,发布的 JSON 无效。您需要添加括号并删除用户部分中的最后一个逗号。所以改变

   "created_at":"Thu Apr 30 10:47:49 +0000 2015",
   "id":593728455901990912,
   "user":{  
      "id":12,
      "name":"GiGi",
      "user_date":"Thu May 17 10:47:49 +0000 2010",
   }

{
    "created_at": "Thu Apr 30 10:47:49 +0000 2015",
    "id": 593728455901990900,
    "user": {
        "id": 12,
        "name": "GiGi",
        "user_date": "Thu May 17 10:47:49 +0000 2010"
    }
}

您还需要将 getter 和 setter 添加到 Author 类,并使属性为非静态属性,以便解析器(在本例中为 GSON)设置属性。

那么只需按用户对消息进行分组:

        Map<Long, List<Message>> userGroup = new HashMap<>();
        for (Message message : messages) {
            List<Message> userMessages = userGroup.get(message.author.getId());
            if (userMessages == null) {
                userMessages = new ArrayList<>();
                userGroup.put(message.author.getId(), userMessages);
            }
            userMessages.add(message);
        }
        for (Entry<Long, List<Message>> entry : userGroup.entrySet()) {
            for (Message message : entry.getValue()) {
                System.out.println(entry.getKey() + " : " + message.date);
            }
        }

更新

您可以通过将equalshashCode 方法添加到Author 类来键入Author。像这样的:

@Override
public int hashCode() {
    int hash = 7;
    hash = 23 * hash + (int) (this.id ^ (this.id >>> 32));
    return hash;
}

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final Author other = (Author) obj;
    if (this.id != other.id) {
        return false;
    }
    return true;
}

这样您就可以按Author 而不是Long 进行分组

Map<Author, List<Message>> userGroup = new HashMap<>();
for (Message message : messages) {
    List<Message> userMessages = userGroup.get(message.author);
    if (userMessages == null) {
        userMessages = new ArrayList<>();
        userGroup.put(message.author, userMessages);
    }
    userMessages.add(message);
}
for (Entry<Author, List<Message>> entry : userGroup.entrySet()) {
    System.out.println(entry.getKey().getId());
    for (Message message : entry.getValue()) {
        System.out.println("\t" + message.date);
    }
}

【讨论】:

  • 是的,这几乎是正确的。这样我就有了一个地图,其中作者 ID 作为键,消息列表作为值,但是如果我想显示作者的姓名和 ID 以及相应的消息列表怎么办?
  • 只需使用列表中第一条消息中的作者,或者根据您想要执行的操作,在您遍历它们时从每条消息中获取它。另一种方法是在 Author 类中放入 equals 和 hash 方法,并将其用作映射中的键。
【解决方案2】:

考虑这个json文件example.json

 [
    { 
     "date":"Thu Apr 30 10:47:49 +0000 2015",
     "id":593728455901990912,
     "author":{  
           "id":12,
            "name":"GiGi",
            "user_date":"Thu May 17 10:47:49 +0000 2010"
            } 
    },

    { 
     "date":"Thu Apr 30 10:47:49 +0000 2013",
     "id":593728455901990977,
     "author":{  
           "id":12,
            "name":"GiGi",
            "user_date":"Thu May 17 10:47:49 +0000 2010"
            } 
    },

    {
      "date":"Thu Apr 30 10:47:49 +0000 2015",
      "id":593728422901330999,
      "author":{  
            "id":13,
             "name":"HiHi",
             "user_date":"Thu May 17 10:47:49 +0000 2015"
              }
    }
 ]

作者类

class Author {    
    private long id;
    private String name;
    private String user_date;
    public String toString() {
        return name + " " + id + " " + user_date;
    }    
   //getters, setters,...   
}

消息类

class Message {
    String date;
    long id;
    Author author;
    public String toString() {
        return id + " " + date;
    }
   //getters, setters,...
}

使用 Gson API 解析 json

Gson gson = new Gson();
List<Message> list = gson.fromJson(new BufferedReader(new FileReader(
            "example.json")), new TypeToken<List<Message>>() {
}.getType());

显示按用户 ID 分组的消息

Map<Long, List<Message>> groupedMap = list.stream().collect(
            Collectors.groupingBy(m->  m.getAuthor().getId()));

groupedMap.forEach((k, v) -> System.out.println(k + " => " + v));

输出:

12 => [593728455901990912 Thu Apr 30 10:47:49 +0000 2015, 593728455901990977 Thu Apr 30 10:47:49 +0000 2013]
13 => [593728422901330999 Thu Apr 30 10:47:49 +0000 2015]

【讨论】:

  • 不幸的是,json 文件的格式有点不同,因为我从流 API 获得了 json 响应。我可以通过重新格式化文件来克服这个问题。 Java 8 中的收集器是一个很好的解决方案,但它给了我一个 NPE 异常,而且我更希望该解决方案基于 Java 7
【解决方案3】:

使用 Jackson,它使您能够通过使用注释将 json 与 POJO 对象匹配。

【讨论】:

  • 我能够解析 json(使用 GSON 库),但无法显示按用户分组的消息
  • 让你的 Message 扩展 hashmap 并覆盖 put 方法,在这种情况下你可以读取用户并将其添加到 hashmap
  • Message 扩展 HashMap 时,此时出现异常:Message message = gson.fromJson(jsonmessage, Message.class); > java.lang.IllegalStateException:应为字符串,但为 BEGIN_OBJECT
猜你喜欢
  • 2020-09-19
  • 2019-05-03
  • 2015-02-27
  • 1970-01-01
  • 2022-12-16
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多