未验证 提交 3c9c424a 编写于 作者: 梦境迷离's avatar 梦境迷离 提交者: GitHub

add json (#5)

* add sonar, json annotation

* add sonar, json annotation

* add sonar, json annotation

* add sonar, json annotation

* add sonar, json annotation

* add sonar, json annotation

* add sonar, json annotation

* add sonar, json annotation

* add coverallsapp

* add coverallsapp
上级 9388de3a
...@@ -17,10 +17,10 @@ jobs: ...@@ -17,10 +17,10 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up JDK 1.8 - name: Set up JDK 11
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 1.8 java-version: 11
- name: Publish Local - name: Publish Local
run: sbt +publishLocal run: sbt +publishLocal
...@@ -29,5 +29,4 @@ jobs: ...@@ -29,5 +29,4 @@ jobs:
run: sbt +compile run: sbt +compile
- name: Run Test - name: Run Test
run: sbt test run: sbt test
\ No newline at end of file
...@@ -32,6 +32,32 @@ println(new TestClass(1, 2)); ...@@ -32,6 +32,32 @@ println(new TestClass(1, 2));
|false|```TestClass(1, 2)``` |```TestClass(i=0, j=2)```| |false|```TestClass(1, 2)``` |```TestClass(i=0, j=2)```|
|true|```TestClass(1, 2, 0, hello, world)```|```TestClass(i=1, j=2, y=0, z=hello, x=world)```| |true|```TestClass(1, 2, 0, hello, world)```|```TestClass(i=1, j=2, y=0, z=hello, x=world)```|
## @json
The `@json` scala macro annotation is the quickest way to add a JSON format to your Play project's case classes.
- Note
- This annotation is drawn from [json-annotation](https://github.com/kifi/json-annotation) and have some
optimization.
- It can also be used when there are other annotations on the case classes.
- Only an implicit object was generated automatically(Maybe need a companion object), and there are no other
operations.
- Example
```scala
@json case class Person(name: String, age: Int)
```
You can now serialize/deserialize your objects using Play's convenience methods:
```scala
import play.api.libs.json._
val person = Person("Victor Hugo", 46)
val json = Json.toJson(person)
Json.fromJson[Person](json)
```
# How to use # How to use
Add library dependency Add library dependency
...@@ -44,6 +70,7 @@ The artefacts have been uploaded to Maven Central. ...@@ -44,6 +70,7 @@ The artefacts have been uploaded to Maven Central.
| Library Version | Scala 2.11 | Scala 2.12 | Scala 2.13 | | Library Version | Scala 2.11 | Scala 2.12 | Scala 2.13 |
|---------|------------|------------|------------| |---------|------------|------------|------------|
| 0.0.2 | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.11/0.0.3)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.11/0.0.3/jar) | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.12/0.0.3)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.12/0.0.3/jar) | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.3)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.3/jar) |
| 0.0.2 | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.11/0.0.2)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.11/0.0.2/jar) | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.12/0.0.2)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.12/0.0.2/jar) | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.2)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.2/jar) | | 0.0.2 | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.11/0.0.2)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.11/0.0.2/jar) | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.12/0.0.2)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.12/0.0.2/jar) | [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.2)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.2/jar) |
| 0.0.1 |-|-| [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.1)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.1/jar) | | 0.0.1 |-|-| [![Maven Central](https://img.shields.io/maven-central/v/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.1)](https://search.maven.org/artifact/io.github.jxnu-liguobin/scala-macro-tools_2.13/0.0.1/jar) |
......
...@@ -15,6 +15,7 @@ lazy val root = (project in file(".")) ...@@ -15,6 +15,7 @@ lazy val root = (project in file("."))
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.scala-lang" % "scala-compiler" % scalaVersion.value, "org.scala-lang" % "scala-compiler" % scalaVersion.value,
"org.scala-lang" % "scala-reflect" % scalaVersion.value, "org.scala-lang" % "scala-reflect" % scalaVersion.value,
"com.typesafe.play" %% "play-json" % "2.7.4" % Test,
"org.scalatest" %% "scalatest" % "3.0.9" % Test "org.scalatest" %% "scalatest" % "3.0.9" % Test
), Compile / scalacOptions ++= { ), Compile / scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match { CrossVersion.partialVersion(scalaVersion.value) match {
...@@ -22,6 +23,7 @@ lazy val root = (project in file(".")) ...@@ -22,6 +23,7 @@ lazy val root = (project in file("."))
case _ => List("-Ymacro-annotations" /*, "-Ymacro-debug-verbose"*/) case _ => List("-Ymacro-annotations" /*, "-Ymacro-debug-verbose"*/)
} }
}, },
Test / testOptions += Tests.Argument("-oDF"),
releaseIgnoreUntrackedFiles := true, releaseIgnoreUntrackedFiles := true,
releaseCrossBuild := true, releaseCrossBuild := true,
releaseProcess := Seq[ReleaseStep]( releaseProcess := Seq[ReleaseStep](
...@@ -54,15 +56,12 @@ lazy val `examples212` = (project in file("examples212")).settings(scalaVersion ...@@ -54,15 +56,12 @@ lazy val `examples212` = (project in file("examples212")).settings(scalaVersion
"io.github.jxnu-liguobin" %% "scala-macro-tools" % (version in ThisBuild).value, "io.github.jxnu-liguobin" %% "scala-macro-tools" % (version in ThisBuild).value,
)).settings( )).settings(
publish / skip := true, publish / skip := true,
paradise
)
val paradise = {
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match { libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, n)) => getParadise(n) case Some((2, n)) if n < 13 => Some("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full)
case _ => None case _ => None
}).fold(Seq.empty[ModuleID])(f => Seq(compilerPlugin(f)))) }).fold(Seq.empty[ModuleID])(f => Seq(compilerPlugin(f)))
def getParadise(n: Long): Option[ModuleID] = {
if (n == 12) Option("org.scalamacros" % s"paradise_$scala212" % "2.1.1")
else if (n == 11) Option("org.scalamacros" % s"paradise_$scala211" % "2.1.1") else None
None
} }
...@@ -2,4 +2,5 @@ addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3") ...@@ -2,4 +2,5 @@ addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.3")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.3") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.3")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1")
addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13") addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.13")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.8.2")
\ No newline at end of file addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.3.1")
\ No newline at end of file
package io.github.dreamylost
import scala.annotation.{ StaticAnnotation, compileTimeOnly }
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
/**
* annotation for case classes
*
* @author 梦境迷离
* @since 2021/6/13
* @version 1.0
*/
@compileTimeOnly("enable macro to expand macro annotations")
final class json extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro jsonMacro.impl
}
object jsonMacro {
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def jsonFormatter(className: TypeName, fields: List[Tree]): c.universe.Tree = {
fields.length match {
case 0 => c.abort(c.enclosingPosition, "Cannot create json formatter for case class with no fields")
case _ =>
c.info(c.enclosingPosition, s"jsonFormatter className: $className, field length: ${fields.length}", force = true)
q"implicit val jsonAnnotationFormat = play.api.libs.json.Json.format[$className]"
}
}
def modifiedCompanion(compDeclOpt: Option[ModuleDef], format: Tree, className: TypeName): c.universe.Tree = {
compDeclOpt map { compDecl =>
// Add the formatter to the existing companion object
val q"object $obj extends ..$bases { ..$body }" = compDecl
val o =
q"""
object $obj extends ..$bases {
..$body
$format
}
"""
c.info(c.enclosingPosition, s"modifiedCompanion className: $className, exists obj: $o", force = true)
o
} getOrElse {
// Create a companion object with the formatter
val o = q"object ${className.toTermName} { $format }"
c.info(c.enclosingPosition, s"modifiedCompanion className: $className, new obj: $o", force = true)
o
}
}
def modifiedDeclaration(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): c.Expr[Nothing] = {
val (className, fields) = classDecl match {
case q"$mods class $className(..$fields) extends ..$bases { ..$body }" =>
if (!mods.asInstanceOf[Modifiers].hasFlag(Flag.CASE)) {
c.abort(c.enclosingPosition, s"Annotation is only supported on case class. classDef: $classDecl, mods: $mods")
} else {
c.info(c.enclosingPosition, s"modifiedDeclaration className: $className, fields: $fields", force = true)
(className, fields)
}
case _ => c.abort(c.enclosingPosition, s"Annotation is only supported on case class. classDef: $classDecl")
}
c.info(c.enclosingPosition, s"modifiedDeclaration className: $className, fields: $fields", force = true)
className match {
case t: TypeName =>
val format = jsonFormatter(t, fields.asInstanceOf[List[Tree]])
val compDecl = modifiedCompanion(compDeclOpt, format, t)
c.info(c.enclosingPosition, s"format: $format, compDecl: $compDecl", force = true)
// Return both the class and companion object declarations
c.Expr(
q"""
$classDecl
$compDecl
""")
}
}
annottees.map(_.tree) match {
case (classDecl: ClassDef) :: Nil => modifiedDeclaration(classDecl)
case (classDecl: ClassDef) :: (compDecl: ModuleDef) :: Nil => modifiedDeclaration(classDecl, Some(compDecl))
case _ => c.abort(c.enclosingPosition, "Invalid annottee")
}
}
}
...@@ -5,7 +5,7 @@ import scala.language.experimental.macros ...@@ -5,7 +5,7 @@ import scala.language.experimental.macros
import scala.reflect.macros.whitebox import scala.reflect.macros.whitebox
/** /**
* toString for class * toString for classes
* *
* @author 梦境迷离 * @author 梦境迷离
* @param verbose Whether to enable detailed log. * @param verbose Whether to enable detailed log.
......
package io.github.dreamylost
import org.scalatest.{ FlatSpec, Matchers }
import play.api.libs.json.Json
/**
*
* @author 梦境迷离
* @since 2021/6/18
* @version 1.0
*/
class JsonTest extends FlatSpec with Matchers {
// class must be writed here
@json
case class TestClass1(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some(""))
object TestClass1
@json
case class TestClass2(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some(""))
@json
@SerialVersionUID(1L)
case class TestClass3(val i: Int = 0, var j: Int, x: String, o: Option[String] = Some(""))
"json1" should "println case class, exists companion object" in {
val ret = Json.prettyPrint(Json.toJson(TestClass1(1, 2, "")))
println(ret)
assert(ret == "{\n \"i\" : 1,\n \"j\" : 2,\n \"x\" : \"\",\n \"o\" : \"\"\n}")
}
"json2" should "println case class, no companion object" in {
val json = Json.toJson(TestClass2(1, 2, ""))
val ret = Json.prettyPrint(json)
println(Json.fromJson[TestClass2](json))
println(ret)
assert(ret == "{\n \"i\" : 1,\n \"j\" : 2,\n \"x\" : \"\",\n \"o\" : \"\"\n}")
}
"json3" should "println case class, contains other annotation" in {
val ret = Json.prettyPrint(Json.toJson(TestClass3(1, 2, "")))
println(ret)
assert(ret == "{\n \"i\" : 1,\n \"j\" : 2,\n \"x\" : \"\",\n \"o\" : \"\"\n}")
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册