这个作业属于哪个课程 | 软件工程实践-2023学年-W班 |
---|---|
这个作业要求在哪里 | 软件工程实践第二次作业——个人实战 |
这个作业的目标 | 用java实现对json文件处理的实战小作业 |
文章目录
一、Gitcode项目地址
二、PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 10 | 5 |
Development | 开发 | 500 | 450 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 120 |
• Design Spec | • 生成设计文档 | 20 | 20 |
• Design Review | • 设计复审 | 10 | 10 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 20 | 20 |
• Design | • 具体设计 | 20 | 30 |
• Coding | • 具体编码 | 360 | 400 |
• Code Review | • 代码复审 | 20 | 20 |
• Test | • 测试(自我测试,修改代码,提交修改) | 60 | 70 |
Reporting | 报告 | 30 | 40 |
• Test Repor | • 测试报告 | 10 | 10 |
• Size Measurement | • 计算工作量 | 10 | 5 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 10 | 10 |
合计 | 1220 | 1230 |
三、解题思路描述
3.1处理JSON数据
经过上网查阅资料之后,采用Jackson库来处理JSON文件。
先创建Jackson的ObjectMapper对象,再使用readTree()方法创建JsonNode,获得了一个存有对应文件的JSON数据的JsonNode。
由于JSON数据是已经提前排序好的,用get方法找到所需的数据之后就可以进行其他操作了。
3.2对输入指令的检验
对players这个指令的检验较为简单,对result指令后面所跟的指令是否正确,采取了如下的处理方法:
先得到input.txt文件中的每一行,对每一行都进行split(" ")操作去除一个空格并存在数组中,这样可以获得一个单词数组。
再把需要比较正确性的单词用join方法连成一个字符串,再和已有的赛事作比较,看是否有这个赛事以检查指令的准确性。
3.3文件读写方式
采用BufferedWriter和Files.readAllLines的方法来进行文件的读写。
四、接口设计和实现过程
设计了三个类,分别为DWASearch、Result、Player。DWASearch类包含主函数和对文件读写的处理,Reslut和Player是包含比赛结果和选手信息的两个实体类,用于存储Json里所需的相关信息,都包含了处理Json文件获得List的方法,具体代码在第五点中展示。
五、关键代码展示
5.1 功能一相关代码
/**
* 获取运动员信息
*
* @param path
* @return
*/
public static List<Player> readJsonToPlayers(String path)
{
// D:/javaworkplace/222100211/src/data/athletes.json
ObjectMapper objectMapper = new ObjectMapper();
List<Player> players = new ArrayList<>();
try
{
//读取json文件
JsonNode jsonNode = objectMapper.readTree(new File(path));
//判断根节点是否为数组
if (jsonNode.isArray())
{
for (JsonNode country : jsonNode)
{
//获取并处理Participations数组
for (JsonNode player : country.get("Participations"))
{
//获取一个player的相关信息
Player p = new Player(player.get("Gender").asInt(), player.get("PreferredLastName").asText(),
player.get("PreferredFirstName").asText(), player.get("NAT").asText());
players.add(p);
}
}
}
} catch (IOException e)
{
throw new RuntimeException(e);
}
return players;
}
5.2 功能二相关代码
/**
* 获得某个赛事决赛的结果
*/
public static List<Result> getResults(String path) throws IOException
{
// D:/javaworkplace/222100211/src/data/
ObjectMapper mapper = new ObjectMapper();
List<Result> results = new ArrayList<>();
JsonNode jsonNode = mapper.readTree(new File(path));
for (JsonNode heats : jsonNode.get("Heats"))
{
if (heats.get("Name").asText().equals("Final"))
{
for (JsonNode jsonResult : heats.get("Results"))
{
//获取信息
String fullName = jsonResult.get("FullName").asText();
int rank = jsonResult.get("Rank").asInt();
ArrayList<Double> scores = new ArrayList<>();
for (JsonNode dives : jsonResult.get("Dives"))
{
scores.add(dives.get("DivePoints").asDouble());
}
Result result = new Result(fullName, rank, scores);
results.add(result);
}
}
}
return results;
}
5.3 主函数相关代码
public static void main(String[] args) throws IOException
{
// 检查是否有命令行参数
if (args.length > 0)
{
//输入和输出的文件路径
String input = args[0];
String output = args[1];
// 打开输入流
try (BufferedWriter writer = new BufferedWriter(new FileWriter(output)))
{
// 读取文件的所有行并存储到列表中
List<String> lines = Files.readAllLines(Paths.get(input));
// 遍历列表中的每一行
for (String line : lines)
{
//去除一个" ",以检验指令格式是否正确
String[] strings = line.split(" ");
//检查指令开头
switch (strings[0])
{
case "players":
if (strings.length==1)
{
List<Player> players = Player.readJsonToPlayers("src/data/athletes.json");
for (Player player : players)
{
writer.write(player.printString());
}
}
break;
case "result":
if (strings.length==1)
{
writer.write("N/A\n");
writer.write("-----\n");
break;
}
else
{
//复制strings之后合并成一个String
String[] copy= Arrays.copyOfRange(strings,1,strings.length);
String name = String.join(" ", copy);
//查询是否有这个比赛
if (Result.isCompetition(name))
{
System.out.println(name);
List<Result> results = Result.getResults("src/data/"+name+".json");
for (Result result : results)
{
writer.write(result.printString());
}
}
else
{
writer.write("N/A\n");
writer.write("-----\n");
break;
}
}
break;
default:
writer.write("ERROR\n");
writer.write("-----\n");
break;
}
}
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
else
{
System.out.println("没有提供命令行参数。");
}
}
六、性能改进
原本在主函数中每从input.txt读取一条指令,就开启一个输入流,采用一次请求一次输出的方式。
之后改为一次性读取input.txt的每一条指令,然后提前开启BufferedWriter做统一处理,这样就不会开启多个BufferedWriter,这样逻辑更清晰,也提升了性能。
七、单元测试
采用junit的单元测试方式,编写了测试类PlayerTest对Player类的方法进行测试
八、异常处理
在打开BufferedWriter时用try catch包围
读取json文件时捕获抛出异常
try
{
//读取json文件
JsonNode jsonNode = objectMapper.readTree(new File(path));
//判断根节点是否为数组
if (jsonNode.isArray())
{
for (JsonNode country : jsonNode)
{
//获取并处理Participations数组
for (JsonNode player : country.get("Participations"))
{
//获取一个player的相关信息
Player p = new Player(player.get("Gender").asInt(), player.get("PreferredLastName").asText(),
player.get("PreferredFirstName").asText(), player.get("NAT").asText());
players.add(p);
}
}
}
} catch (IOException e)
{
throw new RuntimeException(e);
}
九、心得体会
1、初步掌握用Jackson处理JSON的方法,取得所需的某一部分数据;并再次运用文件输入输出的相关知识,提高了对文件的处理能力。
2、学会了添加框架支持。
3、认识到了封装的重要性,对封装的作用有了更深理解,以实现代码和数据的复用。