简体   繁体   English

使用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)

}

I want to use Argonaut to parse some JSON and return a Person OR some errors, as a list. 我想使用Argonaut解析一些JSON并以列表形式返回Person或一些错误。 So I need to: 所以我需要:

  1. Read the JSON from a string, and validate the string is correctly formed 从字符串中读取JSON,并验证字符串格式正确
  2. Decode the JSON into a Person, or List of error strings. 将JSON解码为Person或错误字符串列表。

I want to return errors in the form of something I can turn into some more JSON like: 我想以某种形式返回错误,我可以将其转换为更多的JSON,例如:

{
  errors: ["Error1", "Error2"]
}

I first tried using Argonauts decodeValidation method, which returns a Validation[String, X]. 我首先尝试使用Argonauts的encodeValidation方法,该方法返回Validation [String,X]。 Unfortunately, I need a List of errors. 不幸的是,我需要一个错误列表。

Any suggestions? 有什么建议么?

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. 我将其添加为答案,因为这是我从头开始解决问题的方法,但是一段时间以来我一直没有与Argonaut开发保持紧密联系,我很乐意听到有一个问题更好的方法。 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)
}

And then I'd decode the JSON into a (String, Int) pair before creating the Person : 然后在创建Person之前(String, Int)将JSON解码为(String, Int)对:

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)
  }

And then: 接着:

scala> println(decodePerson("""{ "name": "", "age": 1 }"""))
Failure(NonEmptyList(Name too short, Age is under 18))

Note that this doesn't accumulate errors in more complex cases—eg if the value of the name field is a number and age is 1 , you'll only get a single error (the name one). 请注意,这不会在更复杂的情况下累积错误-例如,如果name字段的值是数字并且age1 ,那么您只会得到一个错误( name one)。 Making error accumulation work in cases like that would be considerably more complex. 在这种情况下使错误累积有效得多。

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. 相关地,您还会在Validation上看到关于flatMap的弃用警告,可以将其视为提醒,不会在绑定期间发生累积。 You can tell the compiler that you understand by importing scalaz.Validation.FlatMap._ . 您可以通过导入scalaz.Validation.FlatMap._告诉编译器您了解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM