首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >SnakeYAML scala中的YAML环境变量插值

SnakeYAML scala中的YAML环境变量插值
EN

Stack Overflow用户
提问于 2021-04-06 20:30:40
回答 1查看 1K关注 0票数 0

利用scala中SnakeYAML & Jackson的最佳特性,我将使用以下方法解析YAML文件。此方法支持在YAML中使用锚。

代码语言:javascript
运行
复制
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文件中插入环境变量。如果我们得到以下环境变量时,我们这样做

代码语言:javascript
运行
复制
>>> println(System.getenv())
{PATH=/usr/bin:/bin:/usr/sbin:/sbin, XPC_FLAGS=0x0, SHELL=/bin/bash}

问题是如何在yaml文件中实现环境变量内插,假设我们有以下YAML文件:

代码语言:javascript
运行
复制
path_value: ${PATH}
xpc: ${XPC_FLAGS}
shell_path: ${SHELL}

那么,在解析YAML之后,应该是:

代码语言:javascript
运行
复制
{
   "path_value": "/usr/bin:/bin:/usr/sbin:/sbin",
   "xpc": "0x0",
   "shell_path": "/bin/bash"
}

感谢您的时间和努力的回答,提前!

EN

回答 1

Stack Overflow用户

发布于 2022-04-16 01:27:16

感谢社会各界的意见和指导。下面是使用自定义构造函数并表示的解析器的解决方案:

代码语言:javascript
运行
复制
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解析器,如下所示:

代码语言:javascript
运行
复制
/**
 * 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
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66976066

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档