https://github.com/limansky/beanpuree
Middle layer between JavaBeans and shapeless
https://github.com/limansky/beanpuree
java-bean scala shapeless typelevel
Last synced: 4 months ago
JSON representation
Middle layer between JavaBeans and shapeless
- Host: GitHub
- URL: https://github.com/limansky/beanpuree
- Owner: limansky
- License: apache-2.0
- Created: 2017-03-28T14:57:39.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2025-08-03T08:32:59.000Z (5 months ago)
- Last Synced: 2025-08-03T10:14:53.479Z (5 months ago)
- Topics: java-bean, scala, shapeless, typelevel
- Language: Scala
- Size: 468 KB
- Stars: 46
- Watchers: 4
- Forks: 10
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
BeanPurée
=========
**BeanPurée** is a middle layer between JavaBeans and [shapeless][shapeless].
> NOTE: The library is in active development stage. So the API might be changed.
[](https://github.com/limansky/beanpuree/actions/workflows/ci.yml)
[](https://gitter.im/limansky/beanpuree?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://codecov.io/gh/limansky/beanpuree)
[](https://maven-badges.herokuapp.com/maven-central/me.limansky/beanpuree_2.12)
## Motivation
Even though Scala is compatible with Java, the languages are different, and the
coding styles are different too. In Scala we prefer to use immutable case classes,
but in Java world mutable JavaBeans are common building blocks. Moreover, many
Scala libraries provide API which requires case classes (e.g. different serializers).
As result, we need to have similar model classes for Java and Scala. This library
helps to convert data between JavaBeans and case classes.
## Usage
BeanPurée is available for 2.11, 2.12 and 2.13. You can add it to your project
adding in `build.sbt`
```Scala
libraryDependencies += "me.limansky" %% "beanpuree" % "0.7"
```
If you'd like to use development version:
```Scala
resolvers += Resolver.sonatypeCentralSnapshots
libraryDependencies += "me.limansky" %% "beanpuree" % "0.8-SNAPSHOT"
```
The core of BeanPurée is `BeanGeneric` class, which have a same role with
shapeless `Generic`, but for JavaBeans. Assume we have class:
```Java
public class Foo {
private int a;
private String b;
public int getA() { return a; }
public void setA(int a) { this.a = a; }
public String getB() { return b; }
public void setB(String b) { this.b = b; }
public String toString() {
return "Foo(" + a + ", \"" + b + "\")";
}
}
```
Now we can create `BeanGeneric` instance and convert bean to HList and backward:
```Scala
scala> val gen = BeanGeneric[Foo]
gen: me.limansky.beanpuree.BeanGeneric[Foo]{type Repr = shapeless.::[Int,shapeless.::[String,shapeless.HNil]]}
scala> val foo = gen.from(5 :: "aaa" :: HNil)
foo: Foo = Foo(5, "aaa")
scala> foo.setB("changed")
scala> gen.to(foo)
res2: gen.Repr = 5 :: changed :: HNil
```
Another important thing is `LabelledBeanGeneric`, which is a `LabelledGeneric` adopted
for the beans. It's important to note, that it uses "field names" generated from the
getters names. E.g. `getStartTime` become `startTime` and `isEven` become `even`.
`JavaTypeMapper[J, S]` provides converters for different Java and Scala classes.
There are converter instances from Java numeric classes to corresponding Scala ones.
It also can convert nullable value to `Option`. You can convert `HList`s of convertable
values as well.
```Scala
scala> type JavaType = Integer :: String :: java.lang.Long :: HNil
defined type alias JavaType
scala> type ScalaType = Int :: String :: Option[Long] :: HNil
defined type alias ScalaType
scala> val m = JavaTypeMapper[JavaType, ScalaType]
m: me.limansky.beanpuree.JavaTypeMapper[JavaType,ScalaType] = me.limansky.beanpuree.JavaTypeMapper$$anon$1@41bbc4c4
scala> m.javaToScala(6 :: "test" :: null :: HNil)
res0: ScalaType = 6 :: test :: None :: HNil
scala> m.scalaToJava(42 :: null :: Some(66l) :: HNil)
res1: JavaType = 42 :: null :: 66 :: HNil
```
The next thing is a `BeanConverter` and `StrictBeanConverter` classes. They use
`LabelledBeanGeneric` and `LabelledGeneric` to convert between beans and case classes.
```Scala
scala> case class Bar(a: Int, b: String)
defined class Bar
scala> val conv = BeanConverter[Foo, Bar]
conv: me.limansky.beanpuree.BeanConverter[Foo,Bar] = me.limansky.beanpuree.BeanConverter$$anon$1@4eae0bc5
scala> conv.beanToProduct(foo)
res3: Bar = Bar(5,changed)
scala> conv.productToBean(Bar(15, "bar"))
res4: Foo = Foo(15, "bar")
```
The converters doesn't care about fields order. The difference between these two
classes is that `StrictBeanConverter` requires the same fields of converting classes
having the same types. It means that if the bean uses Java
numeric classes (like java.lang.Integer), the case class also should have the
field with Java class.
`BeanConverter` is more intelligent. It uses `JavaTypeMapper` to convert field types.
You should be careful using it. For example if you have an Integer field in Java class
and Int in Scala, you might get a NullPointerException if the value is null. Use `Option[Int]`
to make it safe.
[shapeless]: http://github.com/milessabin/shapeless