应用程序通常包含某种默认或“开箱即用”的状态或配置,以及用户根据自身需求自定义配置的方式。
例如,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 等优秀组件之上。
评论已关闭。