Converting a [String] List to a Case Class Using Shapeless

I was wondering if anyone could give an idea of ​​the problem I am having. I made the point with some code and an explanation of my problem: https://gist.github.com/tbrown1979/9993f07c8f4fa2786c83

Basically, I am trying to do something that will allow me to convert List [String] to case class. I made a Reader that will allow me to do this, but I ran into a problem when the Reader defined for the case class cannot contain a reader for a separate case class.

Looking at the “non-working example” below - I encounter a problem when, when reading, I don’t know how many items will be out of the list. With the bar that conducts the test, I will need to pull out 2 elements (because Test has two parameters). Is there a way to find out the number of fields that a case class has only from its type? Is there a better way to do this?

Here is an example using Reader. I also included a non-working example.

////Working Example//// case class Foo(a: Int, s: String) object Foo { implicit val FooReader : Reader[Foo] = Reader[Int :: String :: HNil].map(Generic[Foo].from _) } val read: ValidationNel[String, Foo] = Reader.read[Foo](List("12","text")) println(read)//Success(Foo(12, "text")) /////////////////////////// ////Non-working Example//// case class Test(a: Int, b: String) object Test { implicit val TestReader: Reader[Test] = Reader[Int :: String :: HNil].map(Generic[Test].from _) } case class Bar(c: Test) object Bar { implicit val BarReader: Reader[Bar] = Reader[Test :: HNil].map(Generic[Bar].from _) } val barRead = Reader.read[Bar](List("21", "someString")) println(barRead) //Failure(NonEmptyList("Invalid String: List()", "Exepected empty, but contained value")) ////////////////////////// 
+6
source share
1 answer

Something like this works for me (modification of this )

 object ShapelessStringToTypeConverters { import cats._, implicits._, data.ValidatedNel import mouse._, string._, option._ import shapeless._, labelled._ private type Result[A] = ValidatedNel[ParseFailure, A] case class ParseFailure(error: String) trait Convert[V] { def parse(input: String): Result[V] } object Convert { def to[V](input: String)(implicit C: Convert[V]): Result[V] = C.parse(input) def instance[V](body: String => Result[V]): Convert[V] = new Convert[V] { def parse(input: String): Result[V] = body(input) } implicit def booleans: Convert[Boolean] = Convert.instance( s => s.parseBooleanValidated .leftMap(e => ParseFailure(s"Not a Boolean ${e.getMessage}")) .toValidatedNel) implicit def ints: Convert[Int] = Convert.instance( s => s.parseIntValidated .leftMap(e => ParseFailure(s"Not an Int ${e.getMessage}")) .toValidatedNel) implicit def longs: Convert[Long] = Convert.instance( s => s.parseLongValidated .leftMap(e => ParseFailure(s"Not an Long ${e.getMessage}")) .toValidatedNel) implicit def doubles: Convert[Double] = Convert.instance( s => s.parseDoubleValidated .leftMap(e => ParseFailure(s"Not an Double ${e.getMessage}")) .toValidatedNel) implicit def strings: Convert[String] = Convert.instance(s => s.validNel) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sealed trait SchemaMap[A] { def readFrom(input: Map[String, String]): ValidatedNel[ParseFailure, A] } object SchemaMap { def of[A](implicit s: SchemaMap[A]): SchemaMap[A] = s private def instance[A](body: Map[String, String] => Result[A]): SchemaMap[A] = new SchemaMap[A] { def readFrom(input: Map[String, String]): Result[A] = body(input) } implicit val noOp: SchemaMap[HNil] = SchemaMap.instance(_ => HNil.validNel) implicit def parsing[K <: Symbol, V: Convert, T <: HList](implicit key: Witness.Aux[K], next: SchemaMap[T]): SchemaMap[FieldType[K, V] :: T] = SchemaMap.instance { input => val fieldName = key.value.name val parsedField = input .get(fieldName) .cata(entry => Convert.to[V](entry), ParseFailure(s"$fieldName is missing").invalidNel) .map(f => field[K](f)) (parsedField, next.readFrom(input)).mapN(_ :: _) } implicit def classes[A, R <: HList](implicit repr: LabelledGeneric.Aux[A, R], schema: SchemaMap[R]): SchemaMap[A] = SchemaMap.instance { input => schema.readFrom(input).map(x => repr.from(x)) } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sealed trait SchemaList[A] { def readFrom(input: List[String]): ValidatedNel[ParseFailure, A] } object SchemaList { def of[A](implicit s: SchemaList[A]): SchemaList[A] = s private def instance[A](body: List[String] => Result[A]): SchemaList[A] = new SchemaList[A] { def readFrom(input: List[String]): Result[A] = body(input) } implicit val noOp: SchemaList[HNil] = SchemaList.instance(_ => HNil.validNel) implicit def parsing[K <: Symbol, V: Convert, T <: HList](implicit key: Witness.Aux[K], next: SchemaList[T]): SchemaList[FieldType[K, V] :: T] = SchemaList.instance { input => val fieldName = key.value.name val parsedField = input .headOption .cata(entry => Convert.to[V](entry), ParseFailure(s"$fieldName is missing").invalidNel) .map(f => field[K](f)) (parsedField, next.readFrom(input.tail)).mapN(_ :: _) } implicit def classes[A, R <: HList](implicit repr: LabelledGeneric.Aux[A, R], schema: SchemaList[R]): SchemaList[A] = SchemaList.instance { input => schema.readFrom(input).map(x => repr.from(x)) } } } /* case class Foo(a: String, b: Int, c: Boolean) def m: Map[String, String] = Map("a" -> "hello", "c" -> "true", "b" -> "100") def e: Map[String, String] = Map("c" -> "true", "b" -> "a100") val result = SchemaMap.of[Foo].readFrom(m) val lst = List("145164983", "0.01862523", "16.11681596", "21:38:57", "bid") case class Trade0(tid: Long, price: Double, amount: Double, time: String, tpe: String) val result2 = SchemaList.of[Trade0].readFrom(lst) */ 
0
source

Source: https://habr.com/ru/post/989634/


All Articles