使用Argonaut进行Scalaz验证 [英] Scalaz validation with Argonaut
问题描述
我有一个案例类和一个伴随对象:
I have a case class and companion object:
case class Person private(name: String, age: Int)
object Person {
def validAge(age: Int) = {
if (age > 18) age.successNel else "Age is under 18".failureNel
}
def validName(name: String) = {
name.successNel
}
def create(name: String, age: Int) = (validAge(age) |@| validName(name))(Person.apply)
}
我想使用Argonaut解析一些JSON并返回一个Person或一些错误,作为一个列表.所以我需要:
I want to use Argonaut to parse some JSON and return a Person OR some errors, as a list. So I need to:
- 从字符串中读取JSON,并验证字符串格式正确
- 将JSON解码为Person或错误字符串列表.
我想以某种形式返回错误,我可以将其转换为更多的JSON,例如:
I want to return errors in the form of something I can turn into some more JSON like:
{
errors: ["Error1", "Error2"]
}
我首先尝试使用Argonauts的解码验证方法,该方法返回Validation [String,X].不幸的是,我需要一个错误列表.
I first tried using Argonauts decodeValidation method, which returns a Validation[String, X]. Unfortunately, I need a List of errors.
有什么建议吗?
推荐答案
我将其添加为答案,因为这是我从头开始解决问题的方法,但我一直没有密切注意与Argonaut一起开发了一段时间,我很想听听有更好的方法.首先进行设置,它可以解决您的一些小问题,并为名称添加有效条件,以使后面的示例更加有趣:
I'm adding this as an answer because it's how I'd solve the problem off the top of my head, but I haven't been keeping up closely with Argonaut development for a while, and I'd love to hear that there's a better way. First for the setup, which fixes a few little issues in yours, and adds a condition for validity on names to make the examples later more interesting:
import scalaz._, Scalaz._
case class Person private(name: String, age: Int)
object Person {
def validAge(age: Int): ValidationNel[String, Int] =
if (age > 18) age.successNel else "Age is under 18".failureNel
def validName(name: String): ValidationNel[String, String] =
if (name.size >= 3) name.successNel else "Name too short".failureNel
def create(name: String, age: Int) =
(validName(name) |@| validAge(age))(Person.apply)
}
然后在创建Person
之前将JSON解码为(String, Int)
对:
And then I'd decode the JSON into a (String, Int)
pair before creating the Person
:
import argonaut._, Argonaut._
def decodePerson(in: String): ValidationNel[String, Person] =
Parse.decodeValidation(in)(
jdecode2L((a: String, b: Int) => (a, b)
)("name", "age")).toValidationNel.flatMap {
case (name, age) => Person.create(name, age)
}
然后:
scala> println(decodePerson("""{ "name": "", "age": 1 }"""))
Failure(NonEmptyList(Name too short, Age is under 18))
请注意,这不会在更复杂的情况下(例如,如果name
字段的值是一个数字,而age
是1
,则只会得到一个错误(name
一个).在这种情况下使错误累积有效得多.
Note that this doesn't accumulate errors in more complex cases—e.g. if the value of the name
field is a number and age
is 1
, you'll only get a single error (the name
one). Making error accumulation work in cases like that would be considerably more complex.
相关地,您还将在Validation
上看到有关flatMap
的弃用警告,您可以将其视为提醒,说明绑定期间不会发生累积.您可以通过导入scalaz.Validation.FlatMap._
告诉编译器您了解.
Relatedly, you'll also see a deprecation warning about the flatMap
on Validation
, which you can think of as a reminder that accumulation won't happen across the bind. You can tell the compiler that you understand by importing scalaz.Validation.FlatMap._
.
这篇关于使用Argonaut进行Scalaz验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!