From 8f98304eb4b5f1345c33c589f9ac58fd78dee9d8 Mon Sep 17 00:00:00 2001 From: "Vayda, Oleksandr: IT (PRG)" Date: Thu, 12 Oct 2017 14:21:17 +0200 Subject: [PATCH 1/2] SL-62 Fix REST serialization by switching from Salat JSON to Json4s impl --- commons/pom.xml | 4 + .../absa/spline/common/ReflectionUtils.scala | 24 ++-- .../absa/spline/model/expr/Expression.scala | 6 +- parent/pom.xml | 23 +++- .../serialization/JSONSerialization.scala | 4 +- web/pom.xml | 5 +- .../web/html/controller/MainController.scala | 2 +- .../web/json/StringJSONConverters.scala | 70 ++++++++++++ .../controller/ErrorControllerAdvice.scala | 3 +- .../rest/controller/LineageController.scala | 7 +- .../web/salat/StringJSONConverters.scala | 40 ------- .../web/json/StringJSONConvertersSpec.scala | 103 ++++++++++++++++++ .../web/salat/JSONSalatContextSpec.scala | 57 ---------- .../expression/expression-dialog.component.ts | 4 +- 14 files changed, 222 insertions(+), 130 deletions(-) rename web/src/main/scala/za/co/absa/spline/web/salat/JSONSalatContext.scala => commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala (53%) create mode 100644 web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala delete mode 100644 web/src/main/scala/za/co/absa/spline/web/salat/StringJSONConverters.scala create mode 100644 web/src/test/scala/za/co/absa/spline/web/json/StringJSONConvertersSpec.scala delete mode 100644 web/src/test/scala/za/co/absa/spline/web/salat/JSONSalatContextSpec.scala diff --git a/commons/pom.xml b/commons/pom.xml index c820304ba..af17fdbec 100644 --- a/commons/pom.xml +++ b/commons/pom.xml @@ -33,6 +33,10 @@ commons-configuration commons-configuration + + org.scala-lang + scala-reflect + \ No newline at end of file diff --git a/web/src/main/scala/za/co/absa/spline/web/salat/JSONSalatContext.scala b/commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala similarity index 53% rename from web/src/main/scala/za/co/absa/spline/web/salat/JSONSalatContext.scala rename to commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala index 7f0251a25..830af2730 100644 --- a/web/src/main/scala/za/co/absa/spline/web/salat/JSONSalatContext.scala +++ b/commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala @@ -14,22 +14,16 @@ * limitations under the License. */ -package za.co.absa.spline.web.salat +package za.co.absa.spline.common -import java.util.UUID +import scala.reflect.runtime.{universe => ru} -import salat.transformers.CustomTransformer -import za.co.absa.spline.persistence.mongo.serialization.CommonSalatContext - -object JSONSalatContext { - implicit val ctx = new salat.Context with CommonSalatContext { - override val name: String = "JSON Salat Context" - - registerCustomTransformer(new CustomTransformer[UUID, String]() { - override def serialize(uuid: UUID): String = uuid.toString - - override def deserialize(str: String): UUID = UUID.fromString(str) - }) +object ReflectionUtils { + def subClassesOf[T: ru.TypeTag]: List[Class[_]] = { + val tpe: ru.Type = ru.typeOf[T] + val clazz: ru.ClassSymbol = tpe.typeSymbol.asClass + val m: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader) + require(clazz.isSealed && clazz.isTrait) + clazz.knownDirectSubclasses.toList map ((s: ru.Symbol) => m.runtimeClass(s.asClass)) } } - diff --git a/model/src/main/scala/za/co/absa/spline/model/expr/Expression.scala b/model/src/main/scala/za/co/absa/spline/model/expr/Expression.scala index 7a67415f7..9eec962d2 100644 --- a/model/src/main/scala/za/co/absa/spline/model/expr/Expression.scala +++ b/model/src/main/scala/za/co/absa/spline/model/expr/Expression.scala @@ -83,7 +83,7 @@ case class Alias dataType: DataType, children: Seq[Expression] ) extends Expression { - @Persist + val exprType: String = "Alias" override def outputAttributeNames: Seq[String] = Seq(alias) @@ -121,7 +121,6 @@ case class AttributeRemoval children: Seq[Expression] ) extends Expression { - @Persist val exprType: String = "AttributeRemoval" } @@ -156,10 +155,8 @@ case class AttributeReference dataType: DataType ) extends Expression { - @Persist val exprType: String = "AttributeReference" - @Persist val children: Seq[Expression] = Seq.empty override def inputAttributeNames: Seq[String] = Seq(name) @@ -206,6 +203,5 @@ case class UserDefinedFunction children: Seq[Expression] ) extends Expression { - @Persist val exprType: String = "UserDefinedFunction" } diff --git a/parent/pom.xml b/parent/pom.xml index 381e0b889..f4a481d99 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -70,7 +70,7 @@ 2.2.0 1.2.2 1.7.25 - 1.10.0 + 1.11.2 3.2.11 @@ -197,7 +197,6 @@ org.scala-lang scala-library - ${scala.version} provided @@ -209,6 +208,26 @@ + + org.scala-lang + scala-compiler + ${scala.version} + + + org.scala-lang + scala-reflect + ${scala.version} + + + org.scala-lang + scala-library + ${scala.version} + + + org.scala-lang + scalap + ${scala.version} + org.apache.spark spark-core_${scala.compat.version} diff --git a/persistence/hdfs/src/main/scala/za/co/absa/spline/persistence/hdfs/serialization/JSONSerialization.scala b/persistence/hdfs/src/main/scala/za/co/absa/spline/persistence/hdfs/serialization/JSONSerialization.scala index de36e3346..e93fd5f1a 100644 --- a/persistence/hdfs/src/main/scala/za/co/absa/spline/persistence/hdfs/serialization/JSONSerialization.scala +++ b/persistence/hdfs/src/main/scala/za/co/absa/spline/persistence/hdfs/serialization/JSONSerialization.scala @@ -21,9 +21,11 @@ import org.json4s.jackson.Serialization import org.json4s.jackson.Serialization.write object JSONSerialization { - implicit val formats = Serialization.formats(NoTypeHints) ++ org.json4s.ext.JavaTypesSerializers.all + + implicit val formats: Formats = Serialization.formats(NoTypeHints) ++ org.json4s.ext.JavaTypesSerializers.all implicit class EntityToJson[T <: AnyRef](entity: T) { def toJson: String = write(entity) } + } diff --git a/web/pom.xml b/web/pom.xml index f301a0312..21e351968 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -63,9 +63,12 @@ org.scala-lang scala-library - ${scala.version} compile + + org.json4s + json4s-ext_${scala.compat.version} + javax.servlet javax.servlet-api diff --git a/web/src/main/scala/za/co/absa/spline/web/html/controller/MainController.scala b/web/src/main/scala/za/co/absa/spline/web/html/controller/MainController.scala index 667b4cd54..aa0c6f559 100644 --- a/web/src/main/scala/za/co/absa/spline/web/html/controller/MainController.scala +++ b/web/src/main/scala/za/co/absa/spline/web/html/controller/MainController.scala @@ -25,7 +25,7 @@ import za.co.absa.spline.common.ARMImplicits @Controller class MainController { - @RequestMapping(path = Array("/", "/lineage/**"), method = Array(GET, HEAD)) + @RequestMapping(path = Array("/", "/lineage/**", "/dashboard/**"), method = Array(GET, HEAD)) def index = "index" @RequestMapping(path = Array("/build-info"), method = Array(GET), produces = Array("text/x-java-properties")) diff --git a/web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala b/web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala new file mode 100644 index 000000000..b01e162ff --- /dev/null +++ b/web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala @@ -0,0 +1,70 @@ +/* + * Copyright 2017 Barclays Africa Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.spline.web.json + +import java.net.URI + +import org.json4s.ext.UUIDSerializer +import org.json4s.native.Serialization +import org.json4s.{CustomSerializer, DateFormat, DefaultFormats, Formats, FullTypeHints, JNull, JString, Serializer} +import za.co.absa.spline.common.ReflectionUtils.subClassesOf +import za.co.absa.spline.common.TypeFreaks._ +import za.co.absa.spline.model._ + +object StringJSONConverters { + + object URISerializer extends CustomSerializer[URI](_ => + ( { + case JString(s) => new URI(s) + case JNull => null + }, { + case uri: URI => JString(uri.toString) + })) + + object SplineFormats extends Formats { + val dateFormat: DateFormat = DefaultFormats.lossless.dateFormat + + override def typeHintFieldName: String = "_typeHint" + + override val typeHints = FullTypeHints( + subClassesOf[op.Operation] + ++ subClassesOf[expr.Expression] + ++ subClassesOf[dt.DataType]) + + override val customSerializers: List[Serializer[_]] = + UUIDSerializer :: URISerializer :: super.customSerializers + } + + implicit val formats: Formats = SplineFormats + + implicit class JsonToModel(json: String) { + + def fromJson[T <: AnyRef : Manifest]: T = Serialization.read(json) + + def fromJsonArray[T <: AnyRef : Manifest]: Seq[T] = Serialization.read[Seq[T]](json) + } + + implicit class CollectionToJson[T <: AnyRef : Manifest](xs: Traversable[T]) { + def toJsonArray: String = Serialization.write(xs) + } + + implicit class EntityToJson[T <: AnyRef : `not a subtype of`[Traversable[_]]#λ : Manifest](entity: T) { + def toJson: String = Serialization.write(entity) + } + + +} diff --git a/web/src/main/scala/za/co/absa/spline/web/rest/controller/ErrorControllerAdvice.scala b/web/src/main/scala/za/co/absa/spline/web/rest/controller/ErrorControllerAdvice.scala index 9b481b242..6746457b1 100644 --- a/web/src/main/scala/za/co/absa/spline/web/rest/controller/ErrorControllerAdvice.scala +++ b/web/src/main/scala/za/co/absa/spline/web/rest/controller/ErrorControllerAdvice.scala @@ -17,8 +17,7 @@ package za.co.absa.spline.web.rest.controller import za.co.absa.spline.web.logging.ErrorCode -import za.co.absa.spline.web.salat.JSONSalatContext._ -import za.co.absa.spline.web.salat.StringJSONConverters.EntityToJson +import za.co.absa.spline.web.json.StringJSONConverters.EntityToJson import org.springframework.http.HttpStatus.{INTERNAL_SERVER_ERROR, NOT_FOUND} import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.{ControllerAdvice, ExceptionHandler} diff --git a/web/src/main/scala/za/co/absa/spline/web/rest/controller/LineageController.scala b/web/src/main/scala/za/co/absa/spline/web/rest/controller/LineageController.scala index 84c528219..73f1e249d 100644 --- a/web/src/main/scala/za/co/absa/spline/web/rest/controller/LineageController.scala +++ b/web/src/main/scala/za/co/absa/spline/web/rest/controller/LineageController.scala @@ -17,9 +17,7 @@ package za.co.absa.spline.web.rest.controller import java.util.UUID - -import za.co.absa.spline.web.salat.JSONSalatContext._ -import za.co.absa.spline.web.salat.StringJSONConverters +import za.co.absa.spline.web.json.StringJSONConverters import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Controller import org.springframework.web.bind.annotation.RequestMethod._ @@ -28,6 +26,7 @@ import za.co.absa.spline.persistence.api.DataLineageReader import scala.concurrent.Await import scala.concurrent.duration._ +import scala.language.postfixOps @Controller class LineageController @Autowired() @@ -37,7 +36,7 @@ class LineageController @Autowired() import StringJSONConverters._ - @RequestMapping(path = Array("/lineage/descriptors"), method = Array(GET)) + @RequestMapping(path = Array("/dataset/descriptors"), method = Array(GET)) @ResponseBody def lineageDescriptors: String = Await.result(reader.list(), 10 seconds).toSeq.toJsonArray diff --git a/web/src/main/scala/za/co/absa/spline/web/salat/StringJSONConverters.scala b/web/src/main/scala/za/co/absa/spline/web/salat/StringJSONConverters.scala deleted file mode 100644 index ee92428c5..000000000 --- a/web/src/main/scala/za/co/absa/spline/web/salat/StringJSONConverters.scala +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017 Barclays Africa Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.spline.web.salat - -import za.co.absa.spline.common.TypeFreaks._ -import salat.{Context, grater} - -object StringJSONConverters { - - implicit class JsonToModel(json: String)(implicit ctx: Context) { - - def fromJson[T <: AnyRef : Manifest]: T = grater[T] fromJSON json - - def fromJsonArray[T <: AnyRef : Manifest]: Seq[T] = grater[T] fromJSONArray json - } - - implicit class CollectionToJson[T <: AnyRef : Manifest](xs: Traversable[T])(implicit ctx: Context) { - def toJsonArray: String = grater[T] toCompactJSONArray xs - } - - implicit class EntityToJson[T <: AnyRef : `not a subtype of`[Traversable[_]]#λ : Manifest](entity: T)(implicit ctx: Context) { - def toJson: String = grater[T] toCompactJSON entity - } - - -} diff --git a/web/src/test/scala/za/co/absa/spline/web/json/StringJSONConvertersSpec.scala b/web/src/test/scala/za/co/absa/spline/web/json/StringJSONConvertersSpec.scala new file mode 100644 index 000000000..1034c5d0e --- /dev/null +++ b/web/src/test/scala/za/co/absa/spline/web/json/StringJSONConvertersSpec.scala @@ -0,0 +1,103 @@ +/* + * Copyright 2017 Barclays Africa Group Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package za.co.absa.spline.web.json + +import java.net.URI +import java.util.UUID + +import org.json4s.native.JsonMethods._ +import org.scalatest.{FlatSpec, Matchers} +import za.co.absa.spline.model._ + +class StringJSONConvertersSpec extends FlatSpec with Matchers { + + import StringJSONConverters._ + + val aUUID: UUID = UUID fromString "7d46f047-da82-42fa-8e4b-4b085a210985" + + it should "serialize Foo" in { + val json = Foo(aUUID, new URI("http://example.com"), aUUID.toString :: Nil).toJson + json shouldEqual s"""{"id":"$aUUID","uri":"http://example.com","seq":["$aUUID"]}""" + } + + it should "serialize array of Foo's" in { + val json = Seq( + Foo(aUUID, new URI("http://example.com"), aUUID.toString :: Nil), + Foo(aUUID, new URI("http://example.com"), aUUID.toString :: Nil)).toJsonArray + + json shouldEqual + s"""[ + |{"id":"$aUUID","uri":"http://example.com","seq":["$aUUID"]}, + |{"id":"$aUUID","uri":"http://example.com","seq":["$aUUID"]} + |]""".stripMargin.replaceAll("[\n\r]", "") + } + + it should "deserialize Foo" in { + val foo = s"""{"id":"$aUUID","uri":"http://example.com","seq":["$aUUID"]}""".fromJson[Foo] + foo shouldEqual Foo(aUUID, new URI("http://example.com"), aUUID.toString :: Nil) + } + + it should "deserialize array of Foo's" in { + val foos = s"""[ + |{"id":"$aUUID","uri":"http://example.com","seq":["$aUUID"]}, + |{"id":"$aUUID","uri":"http://example.com","seq":["$aUUID"]} + |]""".stripMargin.fromJsonArray[Foo] + foos shouldEqual Seq( + Foo(aUUID, new URI("http://example.com"), aUUID.toString :: Nil), + Foo(aUUID, new URI("http://example.com"), aUUID.toString :: Nil) + ) + } + + it should "serialize AttributeRemoval" in { + val sourceObj = expr.AttributeRemoval(expr.AttributeReference(Attribute(aUUID, "test", dt.Simple("simpleType", nullable = true)))) + val serializedObj = + s""" + |{ + | "_typeHint":"za.co.absa.spline.model.expr.AttributeRemoval", + | "text":"- test", + | "dataType":{ + | "_typeHint":"za.co.absa.spline.model.dt.Simple", + | "name":"simpleType", + | "nullable":true + | }, + | "children":[ + | { + | "_typeHint":"za.co.absa.spline.model.expr.AttributeReference", + | "refId":"$aUUID", + | "name":"test", + | "text":"test", + | "dataType":{ + | "_typeHint":"za.co.absa.spline.model.dt.Simple", + | "name":"simpleType", + | "nullable":true + | } + | } + | ] + |}""".stripMargin + + parse(sourceObj.toJson) shouldEqual parse(serializedObj) + } + + it should "serialize OperationProps" in { + val testProps = op.OperationProps(aUUID, "foo", Seq(aUUID), aUUID) + val serializedProps = s"""{"id":"$aUUID","name":"foo","inputs":["$aUUID"],"output":"$aUUID"}""" + parse(testProps.toJson) shouldEqual parse(serializedProps) + } + +} + +case class Foo(id: UUID, uri: URI, seq: Seq[String]) \ No newline at end of file diff --git a/web/src/test/scala/za/co/absa/spline/web/salat/JSONSalatContextSpec.scala b/web/src/test/scala/za/co/absa/spline/web/salat/JSONSalatContextSpec.scala deleted file mode 100644 index b202674c2..000000000 --- a/web/src/test/scala/za/co/absa/spline/web/salat/JSONSalatContextSpec.scala +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2017 Barclays Africa Group Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package za.co.absa.spline.web.salat - -import java.net.URI -import java.util.UUID -import java.util.UUID.randomUUID - -import org.json4s.native.JsonMethods._ -import org.scalatest.{FlatSpec, Matchers} -import salat.grater -import za.co.absa.spline.model.Attribute -import za.co.absa.spline.model.dt.Simple -import za.co.absa.spline.model.expr.{AttributeReference, AttributeRemoval} - -class JSONSalatContextSpec extends FlatSpec with Matchers { - - val aUUID: UUID = UUID fromString "7d46f047-da82-42fa-8e4b-4b085a210985" - - import JSONSalatContext._ - - it should "serialize" in { - val json = grater[Foo] toCompactJSON Foo(aUUID, new URI("http://example.com")) - json shouldEqual """{"id":"7d46f047-da82-42fa-8e4b-4b085a210985","uri":"http://example.com"}""" - } - - it should "deserialize" in { - val foo = grater[Foo] fromJSON """{"id":"7d46f047-da82-42fa-8e4b-4b085a210985","uri":"http://example.com"}""" - foo shouldEqual Foo(aUUID, new URI("http://example.com")) - } - - it should "serialize AttributeRemoval without any loss of information" in { - val sourceObj = AttributeRemoval(AttributeReference(Attribute(UUID.fromString("0e6ec462-03dd-499f-a334-b49845a3e816"), "test", Simple("simpleType", nullable = true)))) - val serializedObj = """{"_typeHint":"za.co.absa.spline.model.expr.AttributeRemoval","text":"- test","dataType":{"_typeHint":"za.co.absa.spline.model.dt.Simple","name":"simpleType","nullable":true},"children":[{"_typeHint":"za.co.absa.spline.model.expr.AttributeReference","refId":"0e6ec462-03dd-499f-a334-b49845a3e816","name":"test","text":"test","dataType":{"_typeHint":"za.co.absa.spline.model.dt.Simple","name":"simpleType","nullable":true},"children":[],"exprType":"AttributeReference"}],"exprType":"AttributeRemoval"}""" - - val json = grater[AttributeRemoval] toCompactJSON sourceObj - - parse(json) shouldEqual parse(serializedObj) - } - -} - -case class Foo(id: UUID, uri: URI) \ No newline at end of file diff --git a/web/ui/src/app/lineage/details/expression/expression-dialog.component.ts b/web/ui/src/app/lineage/details/expression/expression-dialog.component.ts index 754bbe844..3cd31c2d0 100644 --- a/web/ui/src/app/lineage/details/expression/expression-dialog.component.ts +++ b/web/ui/src/app/lineage/details/expression/expression-dialog.component.ts @@ -55,7 +55,7 @@ export class ExpressionDialogComponent { function buildChildren(ex: IExpression): (any[] | undefined) { let et = typeOfExpr(ex) // todo: improve expression view for specific expression types - return buildChildrenForGenericExpression(ex.children) + return buildChildrenForGenericExpression(ex.children || []) } function buildChildrenForGenericExpression(subExprs: IExpression[]): any[] { @@ -67,7 +67,7 @@ export class ExpressionDialogComponent { id: seq++, name: _.isEmpty(expr.children) ? expr.text // only use it for leaf expressions - : expr.exprType, + : expr.exprType, // todo: this property is not mandatory for any arbitrary expression text: expr.text.replace(/#\d+/g, ""), children: buildChildren(expr) } From 2b75005016c6c9c0d5f059a93b2b5935dd3cbdf7 Mon Sep 17 00:00:00 2001 From: "Vayda, Oleksandr: IT (PRG)" Date: Thu, 12 Oct 2017 16:18:47 +0200 Subject: [PATCH 2/2] SL-62 + scala doc --- .../absa/spline/common/ReflectionUtils.scala | 20 ++++++++++++++----- .../web/json/StringJSONConverters.scala | 16 +++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala b/commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala index 830af2730..09c2929d3 100644 --- a/commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala +++ b/commons/src/main/scala/za/co/absa/spline/common/ReflectionUtils.scala @@ -18,12 +18,22 @@ package za.co.absa.spline.common import scala.reflect.runtime.{universe => ru} +/** + * Reflection utils + */ object ReflectionUtils { + + private val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader) + + /** + * Lists all direct sub-classes of the given trait T + * + * @tparam T sealed trait type + * @return List of Class[_] instances + */ def subClassesOf[T: ru.TypeTag]: List[Class[_]] = { - val tpe: ru.Type = ru.typeOf[T] - val clazz: ru.ClassSymbol = tpe.typeSymbol.asClass - val m: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader) - require(clazz.isSealed && clazz.isTrait) - clazz.knownDirectSubclasses.toList map ((s: ru.Symbol) => m.runtimeClass(s.asClass)) + val clazz: ru.ClassSymbol = ru.typeOf[T].typeSymbol.asClass + require(clazz.isTrait && clazz.isSealed) + clazz.knownDirectSubclasses.toList map ((s: ru.Symbol) => mirror runtimeClass s.asClass) } } diff --git a/web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala b/web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala index b01e162ff..7cdb74c52 100644 --- a/web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala +++ b/web/src/main/scala/za/co/absa/spline/web/json/StringJSONConverters.scala @@ -25,6 +25,22 @@ import za.co.absa.spline.common.ReflectionUtils.subClassesOf import za.co.absa.spline.common.TypeFreaks._ import za.co.absa.spline.model._ +/** + * Implicit JSON serializer/deserializer + *

+ * Usage examples: + *

+  * import StringJSONConverters._
+  *
+  * // Serialize object to JSON
+  * val myObject: FooBar = ???
+  * val json:String == myObject.toJson
+  *
+  * // Deserialize from JSON
+  * val jsonString: String = """{ "foo":42, "bar":777 }"""
+  * val myObject: FooBar = jsonString.fromJson[FooBar]
+  * 
+ */ object StringJSONConverters { object URISerializer extends CustomSerializer[URI](_ =>