groovy解析xml
我住在加拿大,在其他国家/地区从事很多工作,因此当我记账时,我必须将外币的收入和支出转换为加元。 我过去常常通过辛苦地查询加拿大银行的历史汇率来做到这一点,但去年该银行减少了其支持的货币数量。 我决定使用发布货币汇率历史数据的网站和Java Groovy Apache Groovy (这是一种Java平台的编程语言),寻求一种不同且更实用的解决方案。
我发现了两个外汇历史记录站点,这些站点允许以某种形式对其API进行调用(以JSON返回结果)的方式进行一定程度的免费访问,因此我在Groovy中编写了一个小脚本来解析JSON并返回我需要输入的信息在我的簿记程序中。 这是我的方法。
汇率网站及其API
我发现的第一个站点是Fixer.io ,我非常喜欢。 这是一项免费服务,可从欧洲中央银行访问数据。 该API简单易用。 例如,如果我想要加元和印度卢比的汇率,请使用以下URL:
https://api.fixer.io/latest?symbols=CAD,INR
当我对本文进行查询时,它返回以下JSON:
{
"base":"EUR",
"date":"2018-02-15",
"rates":{"CAD":1.5604,"INR":79.849}
}
这表明,2018年2月15日,购买1欧元需要1.5604加元,购买1欧元需要79.849印度卢比。 要弄清楚购买1加元需要多少印度卢比,只需将79.849除以1.5604,得出每加元51.172印度卢比。 有什么会更容易?
更好的是,Fixer.io是开源的,是根据MIT许可证发布的,并且该代码可在GitHub上获得 。
Fixer.io满足了我将印度卢比转换为加元的需要,但不幸的是,它没有为智利比索提供解决方案,因为欧洲中央银行的基本数据仅涵盖“ 32个”货币。
为了获得有关智利比索的信息,我最后选择了Currencylayer ,(目前)提供168种货币的数据。 Currencylayer要求注册并为其更有价值的产品和复杂的操作收取费用,但是美元与167种其他货币之间的基本历史汇率转换是免费的。 由于覆盖面广,我使用Currencylayer撰写了这篇文章。
在Currencylayer上注册为用户提供了一个密钥,该密钥可以授予对所选服务层的访问权限。 假设键为K
,则URL如:
http://apilayer.net/api/historical?access_key=K&date=2018-01-01¤cies=CAD,EUR&format=1
将返回以下JSON:
{
"success":true,
"terms":"https:\/\/currencylayer.com\/terms",
"privacy":"https:\/\/currencylayer.com\/privacy",
"historical":true,
"date":"2018-01-01",
"timestamp":1514851199,
"source":"USD",
"quotes":{
"USDCAD":1.25551,
"USDEUR":0.832296
}
}
使用Groovy访问API并解析JSON结果
Groovy提供了一些简洁的工具来处理URL,数据流和JSON。
从JSON末尾开始,有JSON slurper及其方便的parse()
方法。 JSON slurper parse方法的一种形式将流作为参数。
这很方便,因为从URL端开始,我们可以使用其newInputStream()
方法在URL上打开流 。
假设我们在一个名为urlString
的Groovy字符串变量中构建了URL字符串,即API调用,我们可以打开URL,读取生成的流,并使用以下Groovy代码解析JSON:
def result = (new JsonSlurper()).parse(
new InputStreamReader(
(new URL(urlString)).newInputStream()
)
)
Groovy变量result
是一个映射(即一组键值对),看起来像这样:
[success:true, terms:https://currencylayer.com/terms,
privacy:https://currencylayer.com/privacy, historical:true,
date:2018-01-01, timestamp:1514851199, source:USD, quotes:
[USDCAD:1.25551, USDEUR:0.832296]]
该映射与上面显示的原始JSON完全对应。 例如,关键date
的值是2018-01-01
。
Groovy允许我们通过以下方式访问分配给date
键的值:
result['date']
要么
result.date
两者都将返回值2018-01-01
。
请注意,关键引号本身就是指具有两个键的地图:一个给出美元和加元之间的转换,另一个给出美元与另一种感兴趣的货币之间的转换。 在上述情况下,可以通过以下方式访问它们
result['quotes']['USDCAD'] and
result['quotes']['USDEUR']
要么
result.quotes.USDCAD and
result.quotes.USDEUR.
使用后一种格式,并假设原始金额在Groovy变量amtOrig
,加元金额可以计算为:
def amtCAD = amtOrig / result.quotes.USDEUR * result.quotes.USDCAD
例如,如果我需要转换通过DFW机场时产生的美元支出,则公式更简单:
def amtCAD = amtOrig * result.quotes.USDCAD
真的就是全部。
(几乎)工作脚本
让我们将其转换为可运行的Groovy脚本。 我们将从命令行获取参数并进行检查。 然后,如果一切正常,我们将构建URL字符串,调用API,并以地图格式获取结果。 最后,我们将分解结果图并计算交换。 这是脚本:
import groovy.json.JsonSlurper
// Check to make sure arguments are correct and print usage if not
if (args.size() != 3) {
System.err.println "usage: groovy fx.groovy yyyy-mm-dd amount currency-code"
System.exit(1)
}
// Check arguments for formatting and reasonable values
String dateString = args[0]
if (!(dateString ==~ /(19\d\d|2[01]\d\d)\-(0[1-9]|1[012])\-([012]\d|3[01])/)) { 1
System.err.println "fx.groovy date $dateString not in format yyyy-mm-dd"
System.exit(1)
}
String amountString = args[1]
if (!(amountString ==~ /\d+(\.\d+)?/)) { 1
System.err.println "fx.groovy amount $amountString not numeric"
System.exit(1)
}
String currencyCode = args[2]
if (!(currencyCode ==~ /[A-Z][A-Z][A-Z]/)) { 1
System.err.println "fx.groovy currency-code $currencyCode not three capital letters, e.g. USD,CAD,EUR,GBP"
System.exit(1)
}
// Our free license will only convert through USD so we adjust the currency list
// according to whether the original currency was USD or something else
String currencyCodeList = currencyCode == 'USD' ? 'CAD' : "CAD,${currencyCode}" 2
// The access key code given during site signup
String accKey = 'a5bd4434d2299ecb3612ad297402481c' 3
// Build the URL string for the API call, get the JSON and parse it
String urlString = "http://apilayer.net/api/historical?access_key=$accKey&date=${dateString}¤cies=${currencyCodeList}&format=1" 2
def result = (new JsonSlurper()).parse(new InputStreamReader((new URL(urlString)).newInputStream()))
// Pick apart the values returned and compute the exchange info
BigDecimal amtOrig = new BigDecimal(amountString) 4
if (result.quotes.size() > 1) {
String toUSDString = 'USD' + currencyCode
BigDecimal amtCAD = amtOrig / result.quotes[toUSDString] * 5
result.quotes.USDCAD
println "$amtOrig $currencyCode toUSD ${result.quotes[toUSDString]} toCAD ${result.quotes.USDCAD} $amtCAD" 2
} else {
BigDecimal amtCAD = amtOrig * result.quotes.USDCAD
println "$amtOrig $currencyCode toCAD ${result.quotes.USDCAD} $amtCAD" 2
}
一些评论:
- 这些是正则表达式模式匹配。 是的,我本可以使用
try... catch
块。 - 表示法
... ${foo}...
在Groovy中称为Gstring;${}
被求值,然后该值将其替换为最终字符串。 - 那不是我的访问密钥! 但是看起来有点像。
- 我使用
BigDecimal
而不是double
来进行计算。 - 我正在使用
[toUSDString]
访问值; 这是因为toUSDString
是变量,而不是常量字符串。 - 长长的线有些缠绕。 对于那个很抱歉!
如果您没有经常使用Groovy,上面的某些内容可能看起来有些不可思议,但实际上并非如此。 我希望这会鼓励进一步的学习!
请注意,以美元为基础的人们不必进行中间计算。
最后的警告提示:这些和其他历史汇率是指示性的,通常根据其他数字的平均值计算得出。 因此,如果在2018年2月15日花费1,623捷克克朗购买100欧元,但Fixer.io在该日期为您提供了25.370捷克克朗对欧元的汇率,这不是错误! 您可能在其他金融机构支付了不同的金额。
翻译自: https://opensource.com/article/18/3/groovy-calculate-foreign-exchange
groovy解析xml