利用scala中SnakeYAML & Jackson的最佳特性,我将使用以下方法解析YAML文件。此方法支持在YAML中使用锚。
import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import java.io.{File, FileInputStream}
import org.yaml.snakeyaml.{DumperOptions, LoaderOptions, Yaml}
/**
* YAML Parser using SnakeYAML & Jackson Implementation
*
* @param yamlFilePath : Path to the YAML file that has to be parsed
* @return: JsonNode of YAML file
*/
def parseYaml(yamlFilePath: String): JsonNode = {
// Parsing the YAML file with SnakeYAML - since Jackson Parser does not have Anchors and reference support
val ios = new FileInputStream(new File(yamlFilePath))
val loaderOptions = new LoaderOptions
loaderOptions.setAllowDuplicateKeys(false)
val yaml = new Yaml(
loaderOptions
)
val mapper = new ObjectMapper().registerModules(DefaultScalaModule)
val yamlObj = yaml.loadAs(ios, classOf[Any])
// Converting the YAML to Jackson YAML - since it has more flexibility for traversing through nodes
val jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(yamlObj)
val jsonObj = mapper.readTree(jsonString)
println(jsonString)
jsonObj
}
但是,这目前不支持在YAML文件中插入环境变量。如果我们得到以下环境变量时,我们这样做
>>> println(System.getenv())
{PATH=/usr/bin:/bin:/usr/sbin:/sbin, XPC_FLAGS=0x0, SHELL=/bin/bash}
问题是如何在yaml文件中实现环境变量内插,假设我们有以下YAML文件:
path_value: ${PATH}
xpc: ${XPC_FLAGS}
shell_path: ${SHELL}
那么,在解析YAML之后,应该是:
{
"path_value": "/usr/bin:/bin:/usr/sbin:/sbin",
"xpc": "0x0",
"shell_path": "/bin/bash"
}
感谢您的时间和努力的回答,提前!
发布于 2022-04-16 01:27:16
感谢社会各界的意见和指导。下面是使用自定义构造函数并表示的解析器的解决方案:
import java.io.{File, FileInputStream}
import scala.util.matching.Regex
import com.fasterxml.jackson.databind.{JsonNode, ObjectMapper}
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import org.yaml.snakeyaml.{DumperOptions, LoaderOptions, Yaml}
import org.yaml.snakeyaml.constructor.{AbstractConstruct, Constructor}
import org.yaml.snakeyaml.error.MissingEnvironmentVariableException
import org.yaml.snakeyaml.nodes.Node
import org.yaml.snakeyaml.nodes.ScalarNode
import org.yaml.snakeyaml.nodes.Tag
import org.yaml.snakeyaml.representer.Representer
/**
* Copyright (c) 2008, http://www.snakeyaml.org
* Class dedicated for SnakeYAML Support for Environment Variables
*/
/**
* Construct scalar for format ${VARIABLE} replacing the template with the value from the environment.
*
* @see <a href="https://bitbucket.org/asomov/snakeyaml/wiki/Variable%20substitution">Variable substitution</a>
* @see <a href="https://docs.docker.com/compose/compose-file/#variable-substitution">Variable substitution</a>
*/
class EnvScalarConstructor() extends Constructor {
val ENV_TAG = new Tag("!ENV")
this.yamlConstructors.put(ENV_TAG, new ConstructEnv)
val ENV_regex: Regex = "\\$\\{\\s*((?<name>\\w+)((?<separator>:?(-|\\?))(?<value>\\w+)?)?)\\s*\\}".r
private class ConstructEnv extends AbstractConstruct {
override def construct(node: Node) = {
val matchValue = constructScalar(node.asInstanceOf[ScalarNode])
val patternMatch = ENV_regex.findAllIn(matchValue)
val eval = patternMatch.toString()
val name = patternMatch.group(1)
val value = patternMatch.group(2)
val separator = null
apply(name, separator, if (value != null) value else "", ENV_regex.replaceAllIn(matchValue, getEnv(name)))
}
}
/**
* Implement the logic for missing and unset variables
*
* @param name - variable name in the template
* @param separator - separator in the template, can be :-, -, :?, ?
* @param value - default value or the error in the template
* @param environment - the value from the environment for the provided variable
* @return the value to apply in the template
*/
def apply(name: String, separator: String, value: String, environment: String): String = {
if (environment != null && !environment.isEmpty) return environment
// variable is either unset or empty
if (separator != null) { //there is a default value or error
if (separator == "?") if (environment == null) throw new MissingEnvironmentVariableException("Missing mandatory variable " + name + ": " + value)
if (separator == ":?") {
if (environment == null) throw new MissingEnvironmentVariableException("Missing mandatory variable " + name + ": " + value)
if (environment.isEmpty) throw new MissingEnvironmentVariableException("Empty mandatory variable " + name + ": " + value)
}
if (separator.startsWith(":")) if (environment == null || environment.isEmpty) return value
else if (environment == null) return value
}
""
}
/**
* Get the value of the environment variable
*
* @param key - the name of the variable
* @return value or null if not set
*/
def getEnv(key: String) = sys.env.getOrElse(key, System.getProperty(key, s"UNKNOWN_ENV_VAR-$key"))
}
上述构造函数可用于YAML解析器,如下所示:
/**
* Function that will be used to load the YAML file
* @param yamlFilePath - String with YAML path to read
* @return - FasterXML JsonNode
*/
def parseYaml(yamlFilePath: String): JsonNode = {
val ios = new FileInputStream(new File(yamlFilePath))
// Parsing the YAML file with SnakeYAML - since Jackson Parser does not have Anchors and reference support
val loaderOptions = new LoaderOptions
loaderOptions.setAllowDuplicateKeys(false)
val yaml = new Yaml(
new EnvScalarConstructor,
new Representer,
new DumperOptions,
loaderOptions
)
val mapper = new ObjectMapper().registerModules(DefaultScalaModule)
val yamlObj = yaml.loadAs(ios, classOf[Any])
// Converting the YAML to Jackson YAML - since it has more flexibility
val jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(yamlObj)
val jsonObj = mapper.readTree(jsonString)
println(jsonString)
jsonObj
}
https://stackoverflow.com/questions/66976066
复制相似问题