应用程序通常包含某种默认或“开箱即用”的状态或配置,以及用户根据自身需求自定义配置的方式。
例如,LibreOffice Writer 允许通过菜单栏上的工具 > 选项访问用户数据、字体、语言设置等。一些应用程序(如 LibreOffice)提供点击式用户界面来管理这些设置。有些应用程序,例如 Tracker(GNOME 任务,用于索引文件),使用 XML 文件。还有一些应用程序,特别是基于 JavaScript 的应用程序,使用 JSON,尽管许多人对此表示抗议(例如,这位作者 和 另一位作者)。
在本文中,我将避开关于是否应该使用 JSON 作为配置文件格式的争论,并解释如何使用 Groovy 编程语言解析此类信息。Groovy 基于 Java,但具有不同的设计优先级,这使得 Groovy 感觉更像 Python。
安装 Groovy
由于 Groovy 基于 Java,因此它也需要安装 Java。您可能会在 Linux 发行版的存储库中找到最新且不错的 Java 和 Groovy 版本。或者,您可以按照其网站上的说明安装 Groovy。对于 Linux 用户来说,一个不错的选择是 SDKMan,您可以使用它来获取多个版本的 Java、Groovy 和许多其他相关工具。在本文中,我将使用我的发行版的 OpenJDK11 版本和 SDKMan 的 Groovy 3.0.7 版本。
演示 JSON 配置文件
为了演示,我从 Drupal 获取了这个 JSON——它是 Drupal CMS 使用的主要配置文件——并将其保存在 config.json 文件中
{
"vm": {
"ip": "192.168.44.44",
"memory": "1024",
"synced_folders": [
{
"host_path": "data/",
"guest_path": "/var/www",
"type": "default"
}
],
"forwarded_ports": []
},
"vdd": {
"sites": {
"drupal8": {
"account_name": "root",
"account_pass": "root",
"account_mail": "box@example.com",
"site_name": "Drupal 8",
"site_mail": "box@example.com",
"vhost": {
"document_root": "drupal8",
"url": "drupal8.dev",
"alias": ["www.drupal8.dev"]
}
},
"drupal7": {
"account_name": "root",
"account_pass": "root",
"account_mail": "box@example.com",
"site_name": "Drupal 7",
"site_mail": "box@example.com",
"vhost": {
"document_root": "drupal7",
"url": "drupal7.dev",
"alias": ["www.drupal7.dev"]
}
}
}
}
}这是一个不错的、复杂的 JSON 文件,具有多个结构级别,例如
<>.vdd.sites.drupal8.account_name以及一些列表,例如
<>.vm.synced_folders这里,<> 代表未命名的顶层。让我们看看 Groovy 如何处理它。
使用 Groovy 解析 JSON
Groovy 附带了 groovy.json 包,其中包含各种很酷的东西。其中最好的部分是 JsonSlurper 类,它包含多个 parse() 方法,可以将 JSON 转换为 Groovy Map——一种数据结构,其中的值根据键存储。
这是一个不错的、简短的 Groovy 程序,名为 config1.groovy,它创建一个 JsonSlurper 实例,然后调用其 parse() 方法之一来解析文件中的 JSON 并将其转换为名为 config 的 Map 实例,最后输出该 map
import groovy.json.JsonSlurper
def jsonSlurper = new JsonSlurper()
def config = jsonSlurper.parse(new File('config.json'))
println "config = $config"在终端的命令行中运行此程序
$ groovy config1.groovy
config = [vm:[ip:192.168.44.44, memory:1024, synced_folders:[[host_path:data/, guest_path:/var/www, type:default]], forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 8, site_mail:box@example.com, vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 7, site_mail:box@example.com, vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]]
$输出显示一个顶层 map,其中包含两个键:vm 和 vdd。每个键都引用其自己的值 map。请注意 forwarded_ports 键引用的空列表。
嗯。这很简单,但它只是打印出来。如何访问各个组件?这是另一个程序,展示了如何访问存储在 config.vm.ip 中的值
import groovy.json.JsonSlurper
def jsonSlurper = new JsonSlurper()
def config = jsonSlurper.parse(new File('config.json'))
println "config.vm.ip = ${config.vm.ip}"运行它
$ groovy config2.groovy
config.vm.ip = 192.168.44.44
$是的,这也很容易。这利用了 Groovy 的简写,意思是
config.vm.ip在 Groovy 中等同于
config['vm']['ip']当 config 和 config.vm 都是 Map 的实例时,两者都等同于
config.get("vm").get("ip")在 Java 中。
仅仅处理 JSON 就说到这里。如果您想要一个标准配置并允许用户覆盖它怎么办?在这种情况下,您可能希望在程序中硬编码一个 JSON 配置,然后读取用户配置并覆盖任何标准配置设置。
假设上面的配置是标准的,用户只想覆盖其中的一部分,只是 vm 结构中的 ip 和 memory 值,并将其放在 userConfig.json 文件中
{
"vm": {
"ip": "201.201.201.201",
"memory": "4096",
}
}您可以使用这个程序来做到这一点
import groovy.json.JsonSlurper
def jsonSlurper = new JsonSlurper()
// use parseText() to parse a string rather than reading from a file
// this gives us the “standard configuration”
def standardConfig = jsonSlurper.parseText("""
{
"vm": {
"ip": "192.168.44.44",
"memory": "1024",
"synced_folders": [
{
"host_path": "data/",
"guest_path": "/var/www",
"type": "default"
}
],
"forwarded_ports": []
},
"vdd": {
"sites": {
"drupal8": {
"account_name": "root",
"account_pass": "root",
"account_mail": "box@example.com",
"site_name": "Drupal 8",
"site_mail": "box@example.com",
"vhost": {
"document_root": "drupal8",
"url": "drupal8.dev",
"alias": ["www.drupal8.dev"]
}
},
"drupal7": {
"account_name": "root",
"account_pass": "root",
"account_mail": "box@example.com",
"site_name": "Drupal 7",
"site_mail": "box@example.com",
"vhost": {
"document_root": "drupal7",
"url": "drupal7.dev",
"alias": ["www.drupal7.dev"]
}
}
}
}
}
""")
// print out the standard configuration
println "standardConfig = $standardConfig"
// read in and parse the user configuration information
def userConfig = jsonSlurper.parse(new File('userConfig.json'))
// print out the user configuration information
println "userConfig = $userConfig"
// a function to merge the user configuration with the standard
def mergeMaps(Map input, Map merge) {
merge.each { k, v ->
if (v instanceof Map)
mergeMaps(input[k], v)
else
input[k] = v
}
}
// merge the configurations and print out the modified
// standard configuration
mergeMaps(standardConfig, userConfig)
println "modified standardConfig $standardConfig"这样运行
$ groovy config3.groovy
standardConfig = [vm:[ip:192.168.44.44, memory:1024, synced_folders:[[host_path:data/, guest_path:/var/www, type:default]], forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 8, site_mail:box@example.com, vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 7, site_mail:box@example.com, vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]]
userConfig = [vm:[ip:201.201.201.201, memory:4096]]
modified standardConfig [vm:[ip:201.201.201.201, memory:4096, synced_folders:[[host_path:data/, guest_path:/var/www, type:default]], forwarded_ports:[]], vdd:[sites:[drupal8:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 8, site_mail:box@example.com, vhost:[document_root:drupal8, url:drupal8.dev, alias:[www.drupal8.dev]]], drupal7:[account_name:root, account_pass:root, account_mail:box@example.com, site_name:Drupal 7, site_mail:box@example.com, vhost:[document_root:drupal7, url:drupal7.dev, alias:[www.drupal7.dev]]]]]]
$以 modified standardConfig 开头的行显示 vm.ip 和 vm.memory 值已被覆盖。
眼尖的读者会注意到,我没有检查格式错误的 JSON,也没有注意确保用户配置有意义(不创建新字段、提供合理的值等等)。因此,合并两个 map 的可爱的小递归方法在现实世界中可能并没有那么实用。
好吧,我总得留点东西作为家庭作业,不是吗?
Groovy 资源
Apache Groovy 网站有很多很棒的文档。另一个很棒的 Groovy 资源是 Mr. Haki。学习 Groovy 的一个非常好的理由是继续学习 Grails,这是一个非常高效的全栈 Web 框架,构建在 Hibernate、Spring Boot 和 Micronaut 等优秀组件之上。

评论已关闭。