beanpuree icon indicating copy to clipboard operation
beanpuree copied to clipboard

Can't find the required implicits

Open aghasemi opened this issue 6 years ago • 13 comments

Hi, Intellij (and Maven) cannot, somehow, find the required implicits when I define a BeanConverter. I have imported me.limansky.beanpuree._. Any idea how I can debug this?

aghasemi avatar Dec 04 '18 16:12 aghasemi

You can try to start your investigation with check if LabelledBeanGeneric instance can be found for your class. And if LabelledGeneric is defined for your case class.

limansky avatar Dec 04 '18 16:12 limansky

Thanks. Seems the issue is with the bean (LabelledBeanGeneric). Is there any criteria bean classes should meet? In my case, the class implements Serializable and fields are protected (instead of private), yet everything else is a typical bean.

aghasemi avatar Dec 05 '18 07:12 aghasemi

Two other suspicions:

  1. Does it support multiple fields with the same type in a bean/case class?
  2. Does it (or JavaTypeMapper) support nesting?

aghasemi avatar Dec 05 '18 10:12 aghasemi

Further tracking down, it seems the problem is the Align class. Any criteria here?

aghasemi avatar Dec 05 '18 12:12 aghasemi

Hm... As I remember Align is using only in StrictBeanConverter. Anyway there no limitations in Align or AlignByKey itself.

If I were you I would try to comment out pairs of getters and setters one by one to find the problem one.

limansky avatar Dec 05 '18 12:12 limansky

Similar issue. I'm using "me.limansky" %% "beanpuree" % "0.4" And for the following test:

package example

import org.scalatest._

import me.limansky.beanpuree._
import me.limansky.beanpuree.BeanConverter
import me.limansky.beanpuree.BeanConverter._

import scala.beans.BeanProperty

class Foo(@BeanProperty var a:Int, @BeanProperty var b: String) {override def toString = s"$a:$b"}
case class Bar(a: Int, b: String) {override def toString = s"$a:$b"}


class ConvTest extends WordSpecLike with Matchers {
  "Suite" should {
    "compare" in {
      val p = Bar(1,"a")
      val b = new Foo(1,"a")
      p.a shouldEqual b.getA
      p.b shouldEqual b.getB
    }
    "convert" in {
      val p = Bar(1,"a")
      val conv = BeanConverter[Foo, Bar]
      val b = conv.productToBean(p)
      p.a shouldEqual b.getA
      p.b shouldEqual b.getB
    }
  }
}

produces error:

beanpuree\CmdParserTest.scala:25:31: could not find implicit value for parameter sbc: me.limansky.beanpuree.BeanConverter[example.Foo,example.Bar]
[error]       val conv = BeanConverter[Foo, Bar]
[error]                               ^
[error] one error found
[error] (Test / compileIncremental) Compilation failed

When I comment out convert, Suite gets passed. What is wrong?

vtitov avatar Dec 25 '18 18:12 vtitov

Hi @vtitov

I've never tried to use beans created as a Scala classes with BeanProperty annotations. It looks like there is some difference between these classes and real Java beans. I'll investigate that. Thanks for isolating problem.

limansky avatar Dec 27 '18 21:12 limansky

I've found that's wrong with your class: it doesn't have default constructor. Thus, it's not a JavaBean by definition: https://en.wikipedia.org/wiki/JavaBeans

They are serializable, have a zero-argument constructor, and allow access to properties using getter and setter methods.

If you change it:

scala> class Foo(@BeanProperty var a:Int, @BeanProperty var b: String) {override def toString = s"$a:$b";  def this() = this(0, "") }
defined class Foo

scala> val gen = BeanGeneric[Foo]
gen: me.limansky.beanpuree.BeanGeneric[Foo]{type Repr = Int :: String :: shapeless.HNil} = $anon$1@8ff9b6f

scala> gen.from(1 :: "aaa" :: HNil)
res2: Foo = 1:aaa

The reason of this behavior is that BeanGeneric (or LabelledBeanGeneric) uses the default constructor to create object from HList. In case of your class the generated code will be like:

val gen = new BeanGeneric[Foo] {
  override type Repr = Int :: String :: HNil
  override def to(foo: Foo): Repr = foo.getA :: foo.getB :: HNil
  override def from(repr: Repr) = repr match {
    case a :: b :: HNil =>
     val foo = new Foo()
     foo.setA(a)
     foo.setB(b)
     foo
  }
}

limansky avatar Dec 27 '18 22:12 limansky

@aghasemi is it the same issue you have?

limansky avatar Dec 27 '18 22:12 limansky

@vtitov Is this kind of classes are really used in your projects? I think it's possible to support it, if it really required.

limansky avatar Dec 29 '18 16:12 limansky

Mike,

No, it were not real classes. I faced similar problem while attempting to use beanpuree from pure Java. I try to use scala adapter object as a workaround.

vtitov avatar Jan 23 '19 10:01 vtitov

@vtitov Could you provide problem Java class?

limansky avatar Jan 24 '19 08:01 limansky

@limansky, the issue was somewhat different:

...ConvTest.scala:15:46: could not find implicit value for parameter sbc: > me.limansky.beanpuree.BeanConverter[B,P] [error] @BeanProperty val converter = BeanConverter[B, P] [error] ^ [error] one error found

scala helper


class JavaBeanConverterHelper[B,P] {
  @BeanProperty val converter = BeanConverter[B, P]
}

java converter

package example;

import me.limansky.beanpuree.*;

public class JavaBeanConverter<B, P> {
    private BeanConverter<B, P> converter = new JavaBeanConverterHelper<B, P>().getConverter();

    public B productToBean(P p) { return converter.productToBean(p); }
    public P beanToProduct(B b) { return converter.beanToProduct(b); }
}

testcase

    "convert-java" in {
      val p = Bar(1,"a")
      val conv = new JavaBeanConverter[Foo, Bar]
      val b = conv.productToBean(p)
      p.a shouldEqual b.getA
      p.b shouldEqual b.getB
    }

vtitov avatar Jan 31 '19 09:01 vtitov