在 Scala 3 中使用 Lift JSON 将 Java 字符串反序列化为 Java 类

问题描述 投票:0回答:1

我正在尝试在 JSON 中为简单的 Scala3 类型(例如 case 类)实现“通用”反序列化 - 我需要传入序列化的 JSON 和目标类的 name (字符串)并接收该实例目标类(之后我将其视为

java.lang.Object
)。

我选择使用Lift框架的JSON库。由于 Lift 仅是 Scala2,我通过 CrossVersion.for3Use2_13 兼容层使用它(这是来自我的 build.sbt):

libraryDependencies += ("net.liftweb" %% "lift-json" % "3.5.0").cross(CrossVersion.for3Use2_13),

我选择 Lift 的 JSON 库是因为可以显式传递目标类进行反序列化(请参阅Extraction Lift 文档):

def extract(json: JValue, target: TypeInfo)(implicit formats: Formats): Any

为了利用它,我首先将类名“取消引用”到 Java 中的 java.lang.Class 实例:

String targetClassName = (inp.entrypointClass == null ? "Main" : inp.entrypointClass);
String mainClassName = this.getClass().getPackage().getName() + "." + targetClassName;
Class targetClass;
try {
  targetClass = Class.forName(mainClassName);
} catch(ClassNotFoundException err1) {
  throw new InvalidEntrypointClassError(err1);
}

这部分似乎运行正确(事实证明我在调试尝试中没有得到

InvalidEntrypointClassError
)。

然后我尝试将字符串反序列化为类,如下所示:

import net.liftweb.json._
import net.liftweb.json.Extraction._
import net.liftweb.json.Serialization._
import reflect._

val formats = net.liftweb.json.DefaultFormats

object ScalaWrapper {
    def deserialize(jsonData: java.lang.String, clazz: java.lang.Class[?]): java.lang.Object = {
        val typeInfo = TypeInfo(clazz, None)
        val data = parse(jsonData)
        val res: Any = extract(data, typeInfo)(formats)
        res.asInstanceOf[java.lang.Object]
    }
}

当我尝试使用简单的示例时,这会失败,例如尝试反序列化到此处定义的

Input
案例类:

case class Node(name: String, left: Option[Int], right: Option[Int])
case class Walk(nodes: List[Node])
case class Input(nodes: List[Node])

请注意,对我来说,拥有“简单”的案例类定义非常重要,即将反序列化问题的复杂性传递给案例类,由于其他要求,这并不是一个解决方案

使用以下 JSON 输入:

{
        "nodes": [
            {
                "name": "A",
                "left": 1,
                "right": 2
            },
            {
                "name": "B"
            },
            {
                "name": "C",
                "left": 4,
                "right": 3
            },
            {
                "name": "D"
            },
            {
                "name": "E"
            }
        ]
    }

错误在 Lift JSON 内部相当深:

java.lang.NullPointerException
        at scala.tools.scalap.scalax.rules.scalasig.ByteCode$.forClass(ClassFileParser.scala:24)
        at scala.tools.scalap.scalax.rules.scalasig.ScalaSigParser$.parse(ScalaSig.scala:68)
        at net.liftweb.json.ScalaSigReader$.findScalaSig(ScalaSig.scala:126)
        at net.liftweb.json.ScalaSigReader$.$anonfun$findScalaSig$1(ScalaSig.scala:126)
        at scala.Option.orElse(Option.scala:477)
        at net.liftweb.json.ScalaSigReader$.findScalaSig(ScalaSig.scala:126)
        at net.liftweb.json.ScalaSigReader$.findClass(ScalaSig.scala:60)
        at net.liftweb.json.ScalaSigReader$.readConstructor(ScalaSig.scala:44)
        ... <SKIPPED ABOUT 20 OTHER STACK FRAMES>
        at net.liftweb.json.Meta$.mappingOf(Meta.scala:195)
        at net.liftweb.json.Extraction$.extract(Extraction.scala:210)
        at binary_tree_dfs.ScalaWrapper$.deserialize(ScalaWrapper.scala:13)
        at binary_tree_dfs.ScalaWrapper.deserialize(ScalaWrapper.scala)

我在调试这个问题时失败了,而且我不知道 Scala 到底发生了什么,但我的猜测是我正在为尝试在 Scala3 项目中使用 Scala2 泛型功能付出代价。 这是正确的吗?这段代码可以在不进行重大重构的情况下修复吗?

由于我不知道这里发生了什么,所以我考虑了其他几种方法:

  • 尝试获取隐式 TypeTag 或 Manifest (我可以发布非工作片段)。它们需要从 Java 端显式传递,而且我没有找到任何关于如何从类name生成这些内容的文档。 这可能吗?
  • 我猜测有一种方法可以将
    izumi-reflect
    TypeTag
    转换为 Scala2
    Manifest
    兼容的对象,但我不知道如何做到这一点,而且我担心这很糟糕且不可靠。 这种方法可行吗?更有意义吗?
  • 或者,我猜我可以使用另一个 Scala3 JSON 库,它可以反序列化为作为(隐式?)参数提供的
    TypeTag
    是否有一个 Scala3 库可以让我做到这一点,而无需修改案例类定义? 也许我可以通过另一种我不明白的方式获得
  • Scala3 Manifest
  • ?关于 Mirror 的 Scala3 文档似乎很少。 我可以构造一个 Scala3 方法来接收类名并返回 java.lang.Class
    Manifest
    的元组吗?如果 Manifest 没有通用参数,这还有意义吗?
    
    
    AFAIU,Scala3 宏是 Scala2 反射的预期替代品。
  • 这可以/应该使用 Scala3 宏来完成吗?
  • 我知道这个问题需要考虑很多,所以感谢您到目前为止:) 我已经尽力提供尽可能多的信息,但又不会过于复杂,但我很乐意分享更多背景信息 - 例如一个我正在担心的不起作用的示例

总结一下问题:

我正在尝试将 JSON 字符串反序列化为我只知道名称的 Scala3 类。 我希望这门课是一个案例课。我目前正在尝试使用 Scala2 的 Lift JSON 库,但失败了。 我希望 JSON 将被反序列化为具有简单定义的案例类,因此我不需要

serialize
/

deserialize
方法或类似方法。
如何在 Scala 3 中实现这一目标?

scala jvm scala-3
1个回答
0
投票
Scala 模块

)能够执行您想要的操作(即解析为运行时已知的类)。 package com.myapp import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.module.scala.{ClassTagExtensions, DefaultScalaModule} case class Node(name: String, left: Option[Int], right: Option[Int]) case class Walk(nodes: List[Node]) case class Input(nodes: List[Node]) val objectMapper = JsonMapper.builder().addModule(DefaultScalaModule).build() :: ClassTagExtensions val input: String = ??? val parsedInput = objectMapper.readValue(input, Class.forName("com.myapp.Input")) println(parsedInput) // Input(List(Node(A,Some(1),Some(2)), Node(B,None,None), Node(C,Some(4),Some(3)), Node(D,None,None), Node(E,None,None)))

© www.soinside.com 2019 - 2024. All rights reserved.