【问题标题】:scala read config to Map of Map of case classscala 将配置读取到案例类 Map 的 Map
【发布时间】:2020-05-18 18:06:56
【问题描述】:

我需要从配置文件中读取并将配置映射到案例类。如果我有一个如下表,它可以正常工作

配置

mapping {
   target {
     oracle  = {
         type = "oracle"
         schema    = "orcl"
         tableName = "my_table"
         query = "select key from my_table where dob='2020-01-01'
     }
}

SCALA 代码片段

 val targetConfig:Map[String,QueryEngine] = config.getObject("mapping.target")
    .entrySet()
    .asScala
    .foldLeft(Map.empty[String , QueryEngine]) { case ( acc , entry ) =>
      val target = entry.getKey
      val targetConfig = entry.getValue match {
        case validElement if validElement.valueType() == ConfigValueType.OBJECT  => validElement.asInstanceOf[ConfigObject].toConfig
        case invalidElement => sys.error("illegal syntax at $invalidElement")
      }

      targetConfig.getString("type")    match {

        case "oracle" => acc + (target ->  new OracleQueryEngine(vars,target,targetConfig.getString("schema"),targetConfig.getString("tableName"),targetConfig.getString("query"),targetConfig.getString("param")))

        case  x   => sys.error(s"unknow target not defined $targetConfig with $targetConfig")
      }
    }

现在我用目标映射中的 MULTIPLE 表更新了 CONFIG。

mapping {
   target {
     oracle  =   
        emp = {
         type = "oracle"
         schema    = "orcl"
         tableName = "emp"
         query = "select key from emp where dob='2020-01-01'
        }
        dept = {
         type = "oracle"
         schema    = "orcl"
         tableName = "dept"
         query = "select key from dept where dob='2020-01-01'
        }
    }
}

多表场景的代码片段 这给出了 mutable.Set[Map[String,QueryEngine]] 的错误表达式不确认 Map[Query,String]

 val targetConfig:Map[String,QueryEngine] = config.getObject("mapping.target")
    .entrySet()
    .asScala
    .foldLeft(Map.empty[String , QueryEngine]) { case ( acc , entry ) =>
      val target = entry.getKey
      val targetConfig = entry.getValue match {
        case validElement if validElement.valueType() == ConfigValueType.OBJECT  => validElement.asInstanceOf[ConfigObject].toConfig
        case invalidElement => sys.error("illegal syntax at $invalidElement")
      }
      targetConfig.getObject(s"mapping.target.$target").keySet().asScala.map { key =>
        target  match {
          case "oracle" => acc + (target ->  new OracleQueryEngine(vars,target,targetConfig.getString("schema"),targetConfig.getString("tableName"),targetConfig.getString("query"),targetConfig.getString("param")))

          case  x   => sys.error(s"unknow target not defined $targetConfig with $targetConfig")
        }
      }

  }

纯配置代码

import pureconfig._
import pureconfig.generic.ProductHint
import pureconfig.generic.auto._
import com.typesafe.config.ConfigFactory
import pureconfig._
import pureconfig.generic.auto._

object PureconfigExample {

  case class QueryEngine(`type`: String, schema: String, tableName: String, query: String)
  type  DatabaseConfig = Map[String, Map[String, QueryEngine]]
  case class TargetConfig(target: DatabaseConfig)
  case class ApplicationConfig(mapping: TargetConfig)

  def main(args: Array[String]): Unit = {
    val configString =
      """
        |mapping {
        |   target {
        |     oracle {
        |        emp {
        |         type = "oracle"
        |         schema    = "orcl"
        |         tableName = "emp"
        |         query = "select key from emp where dob='2020-01-01'"
        |        }
        |        dept  {
        |         type = "oracle"
        |         schema    = "orcl"
        |         tableName = "dept"
        |         query = "select key from dept where dob='2020-01-01'"
        |        }
        |      }
        |    }
        |}
        |""".stripMargin

    import pureconfig.generic.auto._

    // See for more details - https://pureconfig.github.io/docs/overriding-behavior-for-case-classes.html
    implicit def hint[T]: ProductHint[T] = ProductHint[T](ConfigFieldMapping(CamelCase, CamelCase))

    val config = ConfigSource.string(configString).load[ApplicationConfig]
    println(config)
  }
}

POM.XML

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>test2</groupId>
    <artifactId>test2</artifactId>
    <version>1.0-SNAPSHOT</version>


<dependencies>
    <dependency>
        <groupId>com.github.pureconfig</groupId>
        <artifactId>pureconfig-generic-base_2.12</artifactId>
        <version>0.12.2</version>
    </dependency>
    <dependency>
        <groupId>com.github.pureconfig</groupId>
        <artifactId>pureconfig-generic_2.12</artifactId>
        <version>0.12.0</version>
    </dependency>
</dependencies>

</project>

编译器错误

PureConfigExample.scala
Error:(43, 56) Cannot find an implicit instance of pureconfig.ConfigReader[PureconfigExample.ApplicationConfig].
If you are trying to read or write a case class or sealed trait consider using PureConfig's auto derivation by adding `import pureconfig.generic.auto._`
    val config = ConfigSource.string(configString).load[ApplicationConfig]
Error:(43, 56) not enough arguments for method load: (implicit reader: pureconfig.Derivation[pureconfig.ConfigReader[PureconfigExample.ApplicationConfig]])pureconfig.ConfigReader.Result[PureconfigExample.ApplicationConfig].
Unspecified value parameter reader.
    val config = ConfigSource.string(configString).load[ApplicationConfig]

【问题讨论】:

    标签: scala dictionary config scala-collections


    【解决方案1】:

    对不起,这可能不是您所期望的答案,但我强烈建议您避免手动配置解析,因为有些工具会自动为您执行此操作,同时无需样板代码和类型安全。 Scala 生态系统中最流行也可能是最好的库是PureConfig。 所以在你的情况下,解决方案看起来像这样:

    import pureconfig._
    import pureconfig.generic.ProductHint
    import pureconfig.generic.auto._
    
    object PureconfigExample {
    
      case class QueryEngine(`type`: String, schema: String, tableName: String, query: String)
      type  DatabaseConfig = Map[String, Map[String, QueryEngine]]
      case class TargetConfig(target: DatabaseConfig)
      case class ApplicationConfig(mapping: TargetConfig)
    
      def main(args: Array[String]): Unit = {
        val configString =
          """
            |mapping {
            |   target {
            |     oracle {
            |        emp {
            |         type = "oracle"
            |         schema    = "orcl"
            |         tableName = "emp"
            |         query = "select key from emp where dob='2020-01-01'"
            |        }
            |        dept  {
            |         type = "oracle"
            |         schema    = "orcl"
            |         tableName = "dept"
            |         query = "select key from dept where dob='2020-01-01'"
            |        }
            |      }
            |    }
            |}
            |""".stripMargin
    
        // See for more details - https://pureconfig.github.io/docs/overriding-behavior-for-case-classes.html
        implicit def hint[T]: ProductHint[T] = ProductHint[T](ConfigFieldMapping(CamelCase, CamelCase))
    
        val config = ConfigSource.string(configString).load[ApplicationConfig]
        println(config)
      }
    }
    

    在我的情况下会产生下一个结果:

    Right(ApplicationConfig(TargetConfig(Map(oracle -> Map(emp -> QueryEngine(oracle,orcl,emp,select key from emp where dob='2020-01-01'), dept -> QueryEngine(oracle,orcl,dept,select key from dept where dob='2020-01-01'))))))
    

    希望对您有所帮助!

    【讨论】:

    • 感谢 Ivan 的帮助,我尝试使用 pureconfig 版本 0.12.2 进行相同操作,出现错误错误:(44、56)找不到 pureconfig.ConfigReader[PureconfigExample.ApplicationConfig] 的隐式实例。如果您尝试读取或写入案例类或密封特征,请考虑通过添加 import pureconfig.generic.auto._ val config = ConfigSource.string(configString).load[ApplicationConfig] Error:(44, 56) not enough arguments for method 来使用 PureConfig 的自动派生加载:(隐式阅读器:
    • @AjithKannan 感谢您的回复。你有提到所有的进口吗?另外,请确保您同时使用 "com.github.pureconfig" %% "pureconfig""com.github.pureconfig" %% "pureconfig-generic" 依赖项。如果没有任何帮助,请使用新的 pureconfig 代码更新问题。谢谢!
    • 谢谢伊万。我创建了单独的项目并导入了 pureconfig 并得到了编译错误。我用代码和 pom.xml 和错误详细信息更新了问题。谢谢
    • @AjithKannan 从我在您的pom.xml 中看到的情况来看,我猜您当前的问题是您使用不同 Scala 版本的库 - pureconfig-generic_2.13pureconfig-generic-base_2.12 - 这应该对齐并使用pureconfig-generic_2.12 或更新 pureconfig-generic-base_2.13 - 取决于您使用的 Scala 版本。此外,如果您不限于使用 Maven,我建议您使用 sbt - 因为它被视为 Scala 项目的本机解决方案。
    • 我使两个版本相同,即 2.12。现在低于版本的错误消失了,但其他 2 个编译错误仍然存​​在。错误:scalac:加载 auto 时出错,Scala 签名 auto 预期的版本错误:5.0 found: 5.2 in auto.class 。在原始问题中也更新了相同的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-31
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多