Groovy处理XML响应节点:按名称计算平均票税总和
按名称计算XML中票税总和的平均值(排除0值条目)
看起来你已经有了处理XML节点并按名称分组的基础,现在只需要调整逻辑来计算票税总和的平均值,同时排除掉Ticket和Taxes均为0.0的条目。我帮你修改代码来实现这个需求:
首先修正XML结构(原XML存在嵌套错误)
原XML里有一个<Itinerary>嵌套在另一个<Itinerary>中的语法错误,先修正为正确结构:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <SearchRS> <SearchStatus>SUCCESS</SearchStatus> <Itinerary> <Name>Joe</Name> <Ticket>111.11</Ticket> <Taxes>1.11</Taxes> </Itinerary> <Itinerary> <Name>Bob</Name> <Ticket>222.22</Ticket> <Taxes>2.22</Taxes> </Itinerary> <Itinerary> <Name>Joe</Name> <Ticket>333.33</Ticket> <Taxes>3.33</Taxes> </Itinerary> <Itinerary> <Name>Bob</Name> <Ticket>444.44</Ticket> <Taxes>4.44</Taxes> </Itinerary> <Itinerary> <Name>Joe</Name> <Ticket>0.0</Ticket> <Taxes>0.0</Taxes> </Itinerary> </SearchRS>
实现代码
假设doc是你已经解析好的XML文档对象,直接使用以下代码即可得到你需要的结果:
// 1. 过滤无效条目(排除Ticket或Taxes为0.0的条目)并计算单条总费用 def validItineraries = doc.Itinerary.findAll { it -> def ticket = it.Ticket.text() as Double def taxes = it.Taxes.text() as Double ticket != 0.0 && taxes != 0.0 }.collect { it -> [ name: it.Name.text(), totalCost: (it.Ticket.text() as Double) + (it.Taxes.text() as Double) ] } // 2. 按名称分组,计算每个名称的平均费用 def averageByName = validItineraries .inject([:].withDefault { [] }) { map, entry -> map[entry.name] << entry.totalCost map } .collectEntries { name, costs -> def average = costs.sum() / costs.size() [name, average.round(2)] // 保留两位小数,可根据需求调整精度 } // 3. 转换为你期望的列表格式 def resultList = averageByName.collect { name, avgCost -> [name: name, averageCost: avgCost] } // 输出结果 log.info("Average ticket+taxes cost per name: " + resultList)
代码解释
- 过滤与转换:先用
findAll剔除无效条目,再通过collect把每个有效条目转换为包含名称和单条总费用的Map,简化后续计算逻辑。 - 分组求平均:用
inject按名称分组收集所有有效总费用,再通过collectEntries计算每组的平均值(总和除以有效条目数量),并保留两位小数匹配示例格式。 - 格式转换:最后把分组后的Map转换为你需要的列表格式,每个元素包含
name和averageCost字段。
输出结果
运行后会得到完全符合你要求的格式:
[[name:Joe, averageCost:224.44], [name:Bob, averageCost:336.66]]
内容的提问来源于stack exchange,提问作者Peter Cook
相关产品推荐
相关产品推荐

