软件工程实践第二次作业——个人实战

本文描述了一项编程作业,涉及爬取动态网页的JSON数据,使用Jackson解析,创建Player和Result等实体类,实现文件读写,以及设计接口和进行单元测试的过程。作者分享了学习新库和技术的心得体会和项目优化挑战。
摘要由CSDN通过智能技术生成
这个作业属于哪个课程<班级的链接>
这个作业要求在哪里<作业要求的链接>
这个作业的目标实现爬取数据与json文件处理的实战作业

一、Gitcode项目地址

Gitcode项目地址

二、PSP表格

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2022
• Estimate• 估计这个任务需要多少时间55
Development开发300283
• Analysis• 需求分析 (包括学习新技术)120113
• Design Spec• 生成设计文档1010
• Design Review• 设计复审1010
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)2020
• Design• 具体设计2525
• Coding• 具体编码200211
• Code Review• 代码复审2022
• Test• 测试(自我测试,修改代码,提交修改)6048
Reporting报告3030
• Test Repor• 测试报告2010
• Size Measurement• 计算工作量1010
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划1010
合计860829

三、解题思路描述

3.1 爬取json文件

​ 先是尝试了使用jsoup对网页静态爬取,但是给出的网页是动态获取数据的,导致jsoup学得比较浅的我无法爬取数据,之后发现该网页是通过AJAX异步请求加载的数据,于是直接通过XHR获取数据接口来直接获取数据。

XHR获取数据

​ 然后直接把json文件内容复制下来就好了。

3.2 json文件解析及配置

​ 在开始编写代码前,通过了解后决定使用Jackson进行对json文件的解析及使用,并且使用lombok快速生成实体类的部分通用代码以减少代码量。

​ 事实上通过Jackson有两种方法对json文件进行反序列化,一种是通过属性名来对应获取数据;另一种是直接与实体类建立映射关系,快速反序列化。在编写过程中,通过实际使用发现前一种能让程序的耦合度更高(当然也有可能是我对后一种的理解不够导致的)。这会在后面提到。

3.3 文件读写方式

​ 当然还是使用经典款的IO包啦~~

3.4 代码整体结构

​ 本次作业中,布置了三个功能,经过结合所学知识思考后,决定一个功能对应一个实体类和一个返回实体对象List的方法,再直接在main方法对文件进行读写和内容的识别筛选。

四、接口及实体类设计

4.1 功能一的设计

​ 功能一要求从json文件中获取数据并写入output文件中,在这里我选择让json文件与我设计的Player类直接建立映射关系以减少代码量,并按照3.4中所说的建立了类和方法。

4.2 功能二的设计

​ 功能二要求从json文件中获取决赛的排名数据。这里我同样选择了让json文件与我设计的Result类直接建立映射关系。值得一提的是,我还建立了Dive类用于适配json文件中数组之间的嵌套关系,便于该程序将来的功能拓展。

4.3 附加功能的设计

​ 附加功能要求获取对应比赛所有场次的数据。乍一看我以为这个功能只需通过功能二设计的Result类并进行适当的处理即可,但深入分析后,我发现由于我功能二是直接通过映射获取数据,导致只能从决赛中获取数据,使得我不得不从新建DetailResult类及对应方法和修改功能二中的架构之间选择。最后我选择了前一种。

五、关键代码展示

以下代码展示中的实体类均添加了lombok库中的@Data和@NoArgsConstructor

5.1 功能一相关:

/**
 * 运动员类
 */

public class Player {
    @JsonProperty("Gender")
    private int Gender;
    @JsonProperty("PreferredLastName")
    private String PreferredLastName;
    @JsonProperty("PreferredFirstName")
    private String PreferredFirstName;
    @JsonProperty("NAT")
    private String NAT;

    public String toString(){
        return "Full Name:" + PreferredLastName + " " + PreferredFirstName + "\n" +
                "Gender:" + (Gender==0?"Male":"Female") + "\n" +
                "Country:" + NAT + "\n" +
                "-----" + "\n";
    }

}
/**
     * 获取运动员信息
     * @param url 文件路径
     * @return List形式的运动员信息
     * @throws Exception 抛出异常
     */
public static List<Player> getPlayers(String url) throws Exception{
    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    List<Player> players = new ArrayList<>();
    JsonNode rootNode = mapper.readTree(new File(url));

    if(rootNode.isArray()){
        for (JsonNode country : rootNode){
            for (JsonNode p : country.get("Participations")){
                String message = mapper.writeValueAsString(p);
                Player player = mapper.readValue(message,Player.class);
                players.add(player);
            }
        }
    }
    return players;
}

5.2 功能二相关

/**
 * 简略结果类
 */

public class Result {
    @JsonProperty("FullName")
    private String fullName;
    @JsonProperty("Rank")
    private int rank;
    @JsonProperty("TotalPoints")
    private String totalPoints;
    @JsonProperty("Name")
    private String name;
    private List<Dive> points;

    @Override
    public String toString(){
        return  "Full Name:" + fullName + "\n" +
                "Rank:" + rank + "\n" +
                "Score:" + Function.getPoints(points,totalPoints) + "\n" +
                "-----" + "\n";
    }
}
/**
     * 获取简略结果信息
     * @param url 文件路径
     * @return List形式的简略结果信息
     * @throws Exception 抛出异常
     */
public static List<Result> getBriefResults(String url) throws Exception{
    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    List<Result> results = new ArrayList<>();
    JsonNode root = mapper.readTree(new File(url));

    for (JsonNode node : root.get("Heats")) {
        if(node.get("Name").asText().equals("Final")){
            for (JsonNode n : node.get("Results")){
                String message = mapper.writeValueAsString(n);
                Result result = mapper.readValue(message,Result.class);
                List<Dive> dives = new ArrayList<>();
                for (JsonNode d : n.get("Dives")) {
                    message = mapper.writeValueAsString(d);
                    Dive dive = mapper.readValue(message,Dive.class);
                    dives.add(dive);
                }
                result.setPoints(dives);
                results.add(result);
            }
        }
    }

    return results;
}
/**
* 获取对应的分数序列
* @param points 单轮分数
* @param totalPoints 总分
* @return 分数序列
*/
public static String getPoints(List<Dive> points, String totalPoints){
int f = 0;
StringBuilder sb = new StringBuilder("");
for (Dive point : points) {
sb.append(point + (++f == points.size() ? " = " : " + " ));
}
sb.append(totalPoints);
return sb.toString();
}

5.3 附加功能相关

public class DetailResult {
    private String fullName;
    private int[] ranks = new int[3];
    private String[] scores = new String[3];

    public DetailResult(){
        this.ranks[0] = this.ranks[1] = this.ranks[2] = -1;
        this.scores[0] = this.scores[1] = this.scores[2] = "*";
    }

    @Override
    public String toString(){
        return  "Full Name:" + fullName + "\n" +
                "Rank:" + (ranks[0] == -1 ? "*" : ranks[0]) + " | " + (ranks[1] == -1 ? "*" : ranks[1])+ " | " + (ranks[2] == -1 ? "*" : ranks[2]) + "\n" +
                "Preliminary Score:" + scores[0] + "\n" +
                "Semifinal Score:" + scores[1] + "\n" +
                "Final Score:" + scores[2] + "\n" +
                "-----" + "\n";
    }
}
/**
* 获取详细结果信息
* @param url 文件路径
* @return List形式的详细结果信息
* @throws Exception 抛出异常
*/
public static List<DetailResult> getDetailResults(String url) throws Exception{
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Map<String,Integer> mp = Map.ofEntries(
Map.entry("Preliminary",0),
Map.entry("Semifinal",1),
Map.entry("Final",2)
);
List<DetailResult> results = new ArrayList<>();
JsonNode root = mapper.readTree(new File(url));

ArrayNode heatRoot = (ArrayNode) root.get("Heats");
int count = heatRoot.size();
for (int i = count - 1 ; i >= 0 ; i--) {
JsonNode heat = heatRoot.get(i);
String name = heat.get("Name").asText();
for (JsonNode result : heat.get("Results")) {
List<Dive> dives = new ArrayList<>();
for (JsonNode d : result.get("Dives")) {
Dive dive = new Dive(d.get("TotalPoints").asText());
dives.add(dive);
}
DetailResult detail = new DetailResult();
if(name.equals("Preliminary")){
detail.setFullName(result.get("FullName").asText());
results.add(detail);
}
else{
for (DetailResult dr : results) {
if(dr.getFullName().equals(result.get("FullName").asText())){
detail = dr;
break;
}
}
}
int[] ranks = detail.getRanks();
String[] scores = detail.getScores();
ranks[mp.get(name)] = result.get("Rank").asInt();
scores[mp.get(name)] = Function.getPoints(dives,result.get("TotalPoints").asText());
detail.setRanks(ranks);
detail.setScores(scores);
}
}
return results;
}

5.4 主函数(数据处理和文件读写操作)

public static void main(String[] args) {
if (args.length == 2) {
String inputURL = args[0];
String outputURL = args[1];
File inputFile = new File(inputURL);
File outputFile = new File(outputURL);

try (BufferedReader reader = new BufferedReader(new FileReader(inputFile));
FileWriter writer = new FileWriter(outputFile)) {
String line;
while ((line = reader.readLine()) != null) {
String[] words = line.split(" ");
if (words.length == 1 && words[0].equals("players")) {
List<Player> players = Function.getPlayers("./src/data/athletes.json");
StringBuilder sb = new StringBuilder("");
for (Player player : players) {
sb.append(player.toString());
}
writer.write(sb.toString());
} else if (words.length == 1 && words[0].equals("result")) {
writer.write("ERROR\n");
writer.write("-----\n");
} else if (words.length != 1 && words[0].equals("result")) {
StringBuilder sb = new StringBuilder("");
int limit = words.length;
if (words[words.length - 1].equals("detail")) {
limit--;
}
for (int i = 1; i < limit; i++) {
if (i != 1) sb.append(" ");
sb.append(words[i]);
}
String url = "./src/data/" + sb.toString() + ".json";
if (competitionList.indexOf(sb.toString()) != -1) {
var results = (words[words.length - 1].equals("detail") ? Function.getDetailResults(url) : Function.getBriefResults(url));
for (var result : results) {
writer.write(result.toString());
}
} else {
writer.write("N/A\n");
writer.write("-----\n");
}
} else {
writer.write("ERROR\n");
writer.write("-----\n");
}
}
} catch (Exception e) {
System.out.println(e);
}
}
}

六、单元测试

采用junit的单元测试方式,编写了测试类FunctionTest,测试结果如下:

测试数据

七、心得体会

​ 在本次作业中,我深深地体会到了我对完成一个项目所需知识的匮乏。在完成过程中,我大多数时间花在了学习和实践新知识上,包括jsoup,jackson,junit等功能性强大的库,同时对项目结构等知识仍然极度匮乏,并且在设计类结构时,由于对知识的掌握度不足,导致项目的耦合度低,冗余代码量大。只能说道阻且长啊~~

  • 39
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值