Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ lazy val json =
.settings(
description := "JSON Library",
Test / parallelExecution := false,
libraryDependencies ++= Seq(scalap(scalaVersion.value), paranamer, scala_xml)
libraryDependencies ++= Seq(paranamer, scala_xml) ++ {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, _)) => Seq(scalap(scalaVersion.value))
case _ => Seq.empty
}
}
)

lazy val documentationHelpers =
Expand Down
111 changes: 111 additions & 0 deletions core/json/src/main/scala-2.13/net/liftweb/json/JsonASTExtensions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2009-2010 WorldWide Conferencing, LLC
*
* 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 net.liftweb
package json

/** Scala 2.13-specific extensions for JsonAST - supports path-dependent types */
trait JsonASTExtensions {
self: JsonAST.JValue =>

/**
* Find immediate children of this `[[JValue]]` that match a specific `JValue` subclass.
*
* This methid will search a `[[JObject]]` or `[[JArray]]` for values of a specific type and
* return a `List` of those values if they any are found.
*
* So given some JSON like so:
*
* {{{
* [
* {
* "thinga":1,
* "thingb":"bacon"
* },{
* "thingc":3,
* "thingd":"Wakka"
* },{
* "thinge":{
* "thingf":4
* },
* "thingg":true
* }
* ]
* }}}
*
* You would use this method like so:
*
* {{{
* scala> json \ classOf[JInt]
* res0: List[net.liftweb.json.JInt#Values] = List(1, 3)
* }}}
*
* This method does require that whatever type you're searching for is subtype of `JValue`.
*/
def \[A <: JsonAST.JValue](clazz: Class[A]): List[A#Values] =
findDirect(children, typePredicate(clazz) _).asInstanceOf[List[A]] map { _.values }

/**
* Find all descendants of this `JValue` that match a specific `JValue` subclass.
*
* Unlike its cousin `\`, this method will recurse down into all children looking for
* type matches searching a `[[JObject]]` or `[[JArray]]` for values of a specific type and
* return a `List` of those values if they are found.
*
* So given some JSON like so:
*
* {{{
* [
* {
* "thinga":1,
* "thingb":"bacon"
* },{
* "thingc":3,
* "thingd":"Wakka"
* },{
* "thinge":{
* "thingf":4
* },
* "thingg":true
* }
* ]
* }}}
*
* You would use this method like so:
*
* {{{
* scala> json \\ classOf[JInt]
* res0: List[net.liftweb.json.JInt#Values] = List(1, 3, 4)
* }}}
*/
def \\[A <: JsonAST.JValue](clazz: Class[A]): List[A#Values] =
(this filter typePredicate(clazz) _).asInstanceOf[List[A]] map { _.values }

private def findDirect(xs: List[JsonAST.JValue], p: JsonAST.JValue => Boolean): List[JsonAST.JValue] = xs.flatMap {
case JsonAST.JObject(l) =>
l.collect {
case JsonAST.JField(n, x) if p(x) => x
}
case JsonAST.JArray(l) => findDirect(l, p)
case x if p(x) => x :: Nil
case _ => Nil
}

private def typePredicate[A <: JsonAST.JValue](clazz: Class[A])(json: JsonAST.JValue) = json match {
case x if x.getClass == clazz => true
case _ => false
}
}
29 changes: 29 additions & 0 deletions core/json/src/main/scala-2.13/net/liftweb/json/TypeExtractor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.liftweb.json

import scala.reflect.Manifest

/** Abstraction for runtime class and type args (Scala 2.13 variant). */
trait TypeExtractor[A] {
def runtimeClass: Class[_]
def typeArguments: List[TypeExtractor[_]]
}


/** Scala 2.13 implementation backed by Manifest. */
object TypeExtractor extends LowPriorityTypeExtractor {
implicit def fromManifest[A](implicit mf: Manifest[A]): TypeExtractor[A] = new TypeExtractor[A] {
def runtimeClass: Class[_] = mf.runtimeClass
def typeArguments: List[TypeExtractor[_]] = mf.typeArguments.map(arg => fromManifest(arg))
}
}

/** Low-priority fallback kept for compatibility; ClassTag has no type args. */
trait LowPriorityTypeExtractor {
import scala.reflect.ClassTag
implicit def fromClassTag[A](implicit ct: ClassTag[A]): TypeExtractor[A] = new TypeExtractor[A] {
def runtimeClass: Class[_] = ct.runtimeClass
def typeArguments: List[TypeExtractor[_]] = Nil
}
}


135 changes: 135 additions & 0 deletions core/json/src/main/scala-3/net/liftweb/json/JsonASTExtensions.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright 2009-2010 WorldWide Conferencing, LLC
*
* 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 net.liftweb
package json

/** Scala 3-specific extensions for JsonAST - avoids path-dependent type issues */
trait JsonASTExtensions {
self: JsonAST.JValue =>

/**
* Find immediate children of this `[[JValue]]` that match a specific `JValue` subclass.
*
* This method will search a `[[JObject]]` or `[[JArray]]` for values of a specific type and
* return a `List` of those values if they any are found.
*
* So given some JSON like so:
*
* {{{
* [
* {
* "thinga":1,
* "thingb":"bacon"
* },{
* "thingc":3,
* "thingd":"Wakka"
* },{
* "thinge":{
* "thingf":4
* },
* "thingg":true
* }
* ]
* }}}
*
* You would use this method like so:
*
* {{{
* scala> json \ classOf[JInt]
* res0: List[Any] = List(1, 3)
* }}}
*
* This method does require that whatever type you're searching for is subtype of `JValue`.
* Note: In Scala 3, returns List[Any] instead of List[A#Values] for type safety.
*/
def \[A <: JsonAST.JValue](clazz: Class[A]): List[Any] = {
val results = findDirect(children, typePredicate(clazz))
results.asInstanceOf[List[A]].map { jvalue =>
jvalue match {
case JsonAST.JInt(value) => value
case JsonAST.JDouble(value) => value
case JsonAST.JString(value) => value
case JsonAST.JBool(value) => value
case JsonAST.JNull => null
case other => other.values
}
}
}

/**
* Find all descendants of this `JValue` that match a specific `JValue` subclass.
*
* Unlike its cousin `\`, this method will recurse down into all children looking for
* type matches searching a `[[JObject]]` or `[[JArray]]` for values of a specific type and
* return a `List` of those values if they are found.
*
* So given some JSON like so:
*
* {{{
* [
* {
* "thinga":1,
* "thingb":"bacon"
* },{
* "thingc":3,
* "thingd":"Wakka"
* },{
* "thinge":{
* "thingf":4
* },
* "thingg":true
* }
* ]
* }}}
*
* You would use this method like so:
*
* {{{
* scala> json \\ classOf[JInt]
* res0: List[Any] = List(1, 3, 4)
* }}}
*
* Note: In Scala 3, returns List[Any] instead of List[A#Values] for type safety.
*/
def \\[A <: JsonAST.JValue](clazz: Class[A]): List[Any] = {
val filtered = this.filter(typePredicate(clazz))
filtered.asInstanceOf[List[A]].map { jvalue =>
jvalue match {
case JsonAST.JInt(value) => value
case JsonAST.JDouble(value) => value
case JsonAST.JString(value) => value
case JsonAST.JBool(value) => value
case JsonAST.JNull => null
case other => other.values
}
}
}

private def findDirect(xs: List[JsonAST.JValue], p: JsonAST.JValue => Boolean): List[JsonAST.JValue] = xs.flatMap {
case JsonAST.JObject(l) =>
l.collect {
case JsonAST.JField(n, x) if p(x) => x
}
case JsonAST.JArray(l) => findDirect(l, p)
case x if p(x) => x :: Nil
case _ => Nil
}

private def typePredicate[A <: JsonAST.JValue](clazz: Class[A]): JsonAST.JValue => Boolean = { json =>
json.getClass == clazz
}
}
90 changes: 90 additions & 0 deletions core/json/src/main/scala-3/net/liftweb/json/ScalaSig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2009-2010 WorldWide Conferencing, LLC
*
* 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 net.liftweb
package json

import java.util.concurrent.ConcurrentHashMap
import scala.util.{Try, Success, Failure}

private[json] object ScalaSigReader {

def readConstructor(argName: String, clazz: Class[_], typeArgIndex: Int, argNames: List[String]): Class[_] = {
// In Scala 3, we fall back to runtime inspection since compile-time type information
// is not available in the same way as Scala 2's signature parsing.
// For most use cases, this will return AnyRef and the JSON extraction will handle
// type conversion at runtime based on the JSON structure.

try {
// Try to use standard Java reflection to get constructor parameter types
val constructors = clazz.getDeclaredConstructors
val matchingConstructor = constructors.find(_.getParameterCount == argNames.length)

matchingConstructor match {
case Some(constructor) =>
val paramTypes = constructor.getGenericParameterTypes
val argIndex = argNames.indexOf(argName)
if (argIndex >= 0 && argIndex < paramTypes.length) {
paramTypes(argIndex) match {
case cls: Class[_] =>
// Handle generic type arguments - for now, return the raw type
if (typeArgIndex > 0) classOf[AnyRef] else cls
case _ => classOf[AnyRef]
}
} else {
classOf[AnyRef]
}
case None => classOf[AnyRef]
}
} catch {
case _: Exception => classOf[AnyRef]
}
}

def readField(name: String, clazz: Class[_], typeArgIndex: Int): Class[_] = {
try {
// Try to find the field using Java reflection
val field = Try(clazz.getDeclaredField(name)).orElse(
// If direct field lookup fails, try looking in superclasses
Try(findFieldInHierarchy(clazz, name))
)

field match {
case Success(f) =>
f.getGenericType match {
case cls: Class[_] =>
// Handle generic type arguments - for now, return the raw type
if (typeArgIndex > 0) classOf[AnyRef] else cls
case _ => classOf[AnyRef]
}
case Failure(_) => classOf[AnyRef]
}
} catch {
case _: Exception => classOf[AnyRef]
}
}

private def findFieldInHierarchy(clazz: Class[_], name: String): java.lang.reflect.Field = {
if (clazz == null) throw new NoSuchFieldException(name)

try {
clazz.getDeclaredField(name)
} catch {
case _: NoSuchFieldException =>
findFieldInHierarchy(clazz.getSuperclass, name)
}
}
}
Loading