如何使用 Cats 验证来验证 Option 值?
How can I validate Option values with Cats validation?
我正在尝试更改使用猫验证的代码,例如:
case class Example(text: String, image: String)
case class ValidExample(text: String, image: String)
import cats.data.Validated._
import cats.implicits._
def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
val e = Example("test", "test.png")
(validText(e.text), validImage(e.image)).mapN(ValidExample)
效果很好。
但是我的更改要求图像字段是一个选项,例如:
case class Example(text: String, image: Option[String])
case class ValidExample(text: String, image: Option[String])
import cats.data.Validated._
import cats.implicits._
def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
val e = Example("test", Some("test.png"))
(validText(e.text), e.image.map(validImage)).mapN(ValidExample)
有了这个,mapN 失败了,因为类型突然不同了,它说:
value mapN is not a member of (cats.data.Validated[Int,String], Option[cats.data.Validated[Int,String]])
我希望它只验证值是否存在。
因此,如果值存在,它应该是验证结果的一部分,否则忽略该字段。
我知道有一些组合验证的方法,但在我的实际代码中,会比这样复杂得多。
有没有简单的方法来做到这一点?
我无法在文档或搜索中找到任何相关信息。
感谢帮助!
在 Option[Validated[String]]
的地点 e.image.map(validImage)
呼叫 sequence
import cats.data._
import cats.data.Validated._
import cats.implicits._
case class Example(text: String, image: Option[String])
case class ValidExample(text: String, image: Option[String])
def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
val e = Example("test", Some("test.png"))
println((validText(e.text), e.image.map(validImage).sequence).mapN(ValidExample))
产生
Valid(ValidExample(test,Some(test.png)))
答案是traverse
,as usual:):
scala> val exampleWithImage = Example("test", Some("test.png"))
exampleWithImage: Example = Example(test,Some(test.png))
scala> val exampleWithoutImage = Example("test", None)
exampleWithoutImage: Example = Example(test,None)
scala> val badExampleWithImage = Example("test", Some("foo"))
badExampleWithImage: Example = Example(test,Some(foo))
scala> exampleWithImage.image.traverse(validImage)
res1: cats.data.Validated[Int,Option[String]] = Valid(Some(test.png))
scala> exampleWithoutImage.image.traverse(validImage)
res2: cats.data.Validated[Int,Option[String]] = Valid(None)
scala> badExampleWithImage.image.traverse(validImage)
res3: cats.data.Validated[Int,Option[String]] = Invalid(-1)
所以看起来 traverse
on Option
做你想做的事:它验证 Some
的内容,并忽略 None
(即通过它作为有效).
所以你可以这样写,用traverse
代替map
:
scala> (validText(e.text), e.image.traverse(validImage)).mapN(ValidExample)
res4: cats.data.Validated[Int,ValidExample] = Valid(ValidExample(test,Some(test.png)))
大功告成。
我正在尝试更改使用猫验证的代码,例如:
case class Example(text: String, image: String)
case class ValidExample(text: String, image: String)
import cats.data.Validated._
import cats.implicits._
def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
val e = Example("test", "test.png")
(validText(e.text), validImage(e.image)).mapN(ValidExample)
效果很好。
但是我的更改要求图像字段是一个选项,例如:
case class Example(text: String, image: Option[String])
case class ValidExample(text: String, image: Option[String])
import cats.data.Validated._
import cats.implicits._
def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
val e = Example("test", Some("test.png"))
(validText(e.text), e.image.map(validImage)).mapN(ValidExample)
有了这个,mapN 失败了,因为类型突然不同了,它说:
value mapN is not a member of (cats.data.Validated[Int,String], Option[cats.data.Validated[Int,String]])
我希望它只验证值是否存在。 因此,如果值存在,它应该是验证结果的一部分,否则忽略该字段。 我知道有一些组合验证的方法,但在我的实际代码中,会比这样复杂得多。
有没有简单的方法来做到这一点? 我无法在文档或搜索中找到任何相关信息。
感谢帮助!
在 Option[Validated[String]]
的地点 e.image.map(validImage)
sequence
import cats.data._
import cats.data.Validated._
import cats.implicits._
case class Example(text: String, image: Option[String])
case class ValidExample(text: String, image: Option[String])
def validText(text: String) = if (text.nonEmpty) text.valid else invalid(-1)
def validImage(image: String) = if (image.endsWith(".png")) image.valid else invalid(-1)
val e = Example("test", Some("test.png"))
println((validText(e.text), e.image.map(validImage).sequence).mapN(ValidExample))
产生
Valid(ValidExample(test,Some(test.png)))
答案是traverse
,as usual:):
scala> val exampleWithImage = Example("test", Some("test.png"))
exampleWithImage: Example = Example(test,Some(test.png))
scala> val exampleWithoutImage = Example("test", None)
exampleWithoutImage: Example = Example(test,None)
scala> val badExampleWithImage = Example("test", Some("foo"))
badExampleWithImage: Example = Example(test,Some(foo))
scala> exampleWithImage.image.traverse(validImage)
res1: cats.data.Validated[Int,Option[String]] = Valid(Some(test.png))
scala> exampleWithoutImage.image.traverse(validImage)
res2: cats.data.Validated[Int,Option[String]] = Valid(None)
scala> badExampleWithImage.image.traverse(validImage)
res3: cats.data.Validated[Int,Option[String]] = Invalid(-1)
所以看起来 traverse
on Option
做你想做的事:它验证 Some
的内容,并忽略 None
(即通过它作为有效).
所以你可以这样写,用traverse
代替map
:
scala> (validText(e.text), e.image.traverse(validImage)).mapN(ValidExample)
res4: cats.data.Validated[Int,ValidExample] = Valid(ValidExample(test,Some(test.png)))
大功告成。