Scala 在pureconfig中表示
我有一个HOCON配置,如下所示: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:
[
{
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
fallbackRight
或Right
fallbackLeft
有意义吗
- 那[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
fallbackRight
或Right
fallbackLeft
有意义吗
- 那[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开始提供