Scala 在pureconfig中表示

Scala 在pureconfig中表示,scala,config,either,hocon,pureconfig,Scala,Config,Either,Hocon,Pureconfig,我有一个HOCON配置,如下所示: [ { name = 1 url = "http://example.com" }, { name = 2 url = "http://example2.com" }, { name = 3 url = { A = "http://example3.com" B = "http:

我有一个HOCON配置,如下所示:

[
    {
        name = 1
        url = "http://example.com"
    },
    {
        name = 2
        url = "http://example2.com"
    },
    {
        name = 3
        url = {
            A = "http://example3.com"
            B = "http://example4.com"
        }
    }
]
我想用pureconfig解析它。 如何表示URL可以是字符串,也可以是多个URL的映射,每个URL都有一个键

我试过这个:

import pureconfig.ConfigSource
import pureconfig.generic.auto.exportReader

case class Site(name: Int, url: Either[String, Map[String, String]])
case class Config(sites: List[Site])
ConfigSource.default.loadOrThrow[Config]
但结果是“预期的类型对象。改为查找字符串。”


我知道pureconfig支持
选项
。我没有发现任何关于支持
,这是否意味着它可以被其他东西取代?

正如您在“不在列表中”中看到的

但是,
属于以下类别,因此:

工作。如果您有一个密封的层次结构,pureconfig将需要一个具有字段
类型的对象-此字段将用于向特定子类型分派解析。所有其他字段将作为字段传递,以解析为该子类型

如果这对您不起作用,您可以尝试自己实现编解码器:

// just an example
implicit def eitherReader[A: ConfigReader, B: ConfigReader] =
  new ConfigReader[Either[A, B]] {
    def from(cur: ConfigCursor) =
      // try left, if fail try right
      ConfigReader[A].from(cur).map(Left(_)) orElse ConfigReader[B].from(cur).map(Right(_))
  }
现在不需要区分值:

@ ConfigSource.string("""{ test: "test" }""").load[Map[String, Either[String, String]]]
res26: ConfigReader.Result[Map[String, Either[String, String]]] = Right(Map("test" -> Left("test")))
默认情况下不提供此选项,因为您必须自己回答一些问题:

  • 您如何决定是使用
    还是
    解码
  • Left
    fallback
    Right
    Right
    fallback
    Left
    有意义吗
  • 那[X,X]

如果您知道预期的行为,您可以实现您的旧编解码器,并在派生中使用它。

正如您在“不在列表中”中看到的

但是,
属于以下类别,因此:

工作。如果您有一个密封的层次结构,pureconfig将需要一个具有字段
类型的对象-此字段将用于向特定子类型分派解析。所有其他字段将作为字段传递,以解析为该子类型

如果这对您不起作用,您可以尝试自己实现编解码器:

// just an example
implicit def eitherReader[A: ConfigReader, B: ConfigReader] =
  new ConfigReader[Either[A, B]] {
    def from(cur: ConfigCursor) =
      // try left, if fail try right
      ConfigReader[A].from(cur).map(Left(_)) orElse ConfigReader[B].from(cur).map(Right(_))
  }
现在不需要区分值:

@ ConfigSource.string("""{ test: "test" }""").load[Map[String, Either[String, String]]]
res26: ConfigReader.Result[Map[String, Either[String, String]]] = Right(Map("test" -> Left("test")))
默认情况下不提供此选项,因为您必须自己回答一些问题:

  • 您如何决定是使用
    还是
    解码
  • Left
    fallback
    Right
    Right
    fallback
    Left
    有意义吗
  • 那[X,X]

如果您知道预期的行为,您可以实现您的旧编解码器并在派生中使用它。

可能有几种方法,但我不喜欢将两者都用作配置表示。因此,我建议使用具有密封特征的ADT方法:

  sealed trait NameUrl {
    val name: Int
  }

  case class Name(
    name: Int,
    url: String
  ) extends NameUrl

  case class NameUrlObj(
    name: Int,
    url: Map[String, String]
  ) extends NameUrl

对不起,我的名字在这里。这将是配置的一种表示形式。 我们需要修改一下配置,以便使用ADT轻松解析配置。为了支持泛型类型,您应该为每个子类型添加spefici类型名称。 我将在这里提供完整的示例,以便您可以在您的机器上运行它:

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

object TstObj extends App {

  sealed trait NameUrl {
    val name: Int
  }

  case class Name(
    name: Int,
    url: String
  ) extends NameUrl

  case class NameUrlObj(
    name: Int,
    url: Map[String, String]
  ) extends NameUrl

  val cfgStr = ConfigFactory.parseString(
    """
      |abc: [
      |  {
      |    type: name,
      |    name = 1
      |    url = "http://example.com"
      |  },
      |  {
      |    type: name,
      |    name = 1
      |    url = "http://example.com"
      |  },
      |  {
      |    type: name-url-obj,
      |    name = 3
      |    url = {
      |      "A": "http://example3.com"
      |      "B": "http://example4.com"
      |    }
      |  }
      |]
      |""".stripMargin
  )


  case class RootA(abc: List[NameUrl])
  println(ConfigSource.fromConfig(cfgStr).loadOrThrow[RootA])

}

您可以在这里阅读更多关于

的内容。可能有几种方法可以实现这一点,但我不喜欢将这两种方法都用作配置表示。因此,我建议使用具有密封特征的ADT方法:

  sealed trait NameUrl {
    val name: Int
  }

  case class Name(
    name: Int,
    url: String
  ) extends NameUrl

  case class NameUrlObj(
    name: Int,
    url: Map[String, String]
  ) extends NameUrl

对不起,我的名字在这里。这将是配置的一种表示形式。 我们需要修改一下配置,以便使用ADT轻松解析配置。为了支持泛型类型,您应该为每个子类型添加spefici类型名称。 我将在这里提供完整的示例,以便您可以在您的机器上运行它:

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

object TstObj extends App {

  sealed trait NameUrl {
    val name: Int
  }

  case class Name(
    name: Int,
    url: String
  ) extends NameUrl

  case class NameUrlObj(
    name: Int,
    url: Map[String, String]
  ) extends NameUrl

  val cfgStr = ConfigFactory.parseString(
    """
      |abc: [
      |  {
      |    type: name,
      |    name = 1
      |    url = "http://example.com"
      |  },
      |  {
      |    type: name,
      |    name = 1
      |    url = "http://example.com"
      |  },
      |  {
      |    type: name-url-obj,
      |    name = 3
      |    url = {
      |      "A": "http://example3.com"
      |      "B": "http://example4.com"
      |    }
      |  }
      |]
      |""".stripMargin
  )


  case class RootA(abc: List[NameUrl])
  println(ConfigSource.fromConfig(cfgStr).loadOrThrow[RootA])

}

您可以在这里阅读更多关于

的内容是的,这是一个很好的解决方案,但是配置需要有
类型
(或
种类
或其他一些键),这似乎是多余的,由您决定哪种方式更适合您。当然,也不能/不应该是静态数据的表示。是的,这是一个很好的解决方案,但是配置需要有
类型
(或
种类
或其他一些键),这似乎是多余的,这取决于您选择哪种方式更适合您。IMHO,两者都不能/不应该是静态数据的表示。是的,谢谢,这个答案为我指明了方向。特别是带有
orElse
部分的自定义
ConfigReader
。scala 3中的联合类型可能会在generalI get中回答这些问题:“value orElse不是scala.util的成员。或者[pureconfig.error.ConfigReaderFailures,scala.util.Left[a,Nothing]]”带有此变量。我错过了一个导入或类似的东西吗?它是从2.13开始提供的是的,谢谢,这个答案为我指明了方向。特别是带有
orElse
部分的自定义
ConfigReader
。scala 3中的联合类型可能会在generalI get中回答这些问题:“value orElse不是scala.util的成员。或者[pureconfig.error.ConfigReaderFailures,scala.util.Left[a,Nothing]]”带有此变量。我是否错过了导入或类似的内容?它从2.13开始提供