我正在嘗試反序列化scala中的以下字串
{ "nodeid": 30, "depth": 6, "split": 65, "split_condition": -9.53674316e-07, "yes": 43, "no": 44, "missing": 43 , "children": [
{ "nodeid": 43, "leaf": 0.000833201397 },
{ "nodeid": 44, "leaf": -0.00145038881 }
]}
它是一個樹形結構,我們可以認為有非葉子節點和葉子節點,這兩者都擴展了抽象節點型別。它們具有不同的屬性。(這個字串是從 XGBoost 轉儲的,我想通過反序列化來創建自己的資料結構)
我嘗試使用杰克遜,但它只需要一種型別別。如果我定義一個包含所有這些屬性的類,它肯定可以作業,但之后我無法轉儲回相同的格式。
那么除了自定義覆寫deserialize,還有其他更好的選擇嗎?
如果無法得到問題,可以參考我的以下示例
我希望將字串反序列化為Node具有以下定義的類
class Node {
var nodeid: Int = 0
}
class NotLeaf extends Node {
var depth: Int = 0
var split: Int = 0
...
var children: List[Node] = null
}
class Leaf extends Node {
var leaf: Float = 0
}
對于我嘗試過的代碼,我可以定義類
class Node {
var nodeid: Int = 0
var depth: Int = 0
var split: Int = 0
var split_condition: Float = 0
var yes: Int = 0
var no: Int = 0
var missing: Int = 0
var children: List[XGBoostFormat] = null
var leaf: Float = 0
}
并且反序列化結果將具有所有屬性,如果我序列化回來,它將是
{
"nodeid" : 30,
"depth" : 6,
"split" : 65,
"split_condition" : -9.536743E-7,
"yes" : 43,
"no" : 44,
"missing" : 43,
"children" : [ {
"nodeid" : "43",
"depth" : 0,
"split" : 0,
"split_condition" : 0.0,
"yes" : 0,
"no" : 0,
"missing" : 0,
"children" : null,
"leaf" : 8.332014E-4
}, {
"nodeid" : "44",
"depth" : 0,
"split" : 0,
"split_condition" : 0.0,
"yes" : 0,
"no" : 0,
"missing" : 0,
"children" : null,
"leaf" : -0.0014503888
} ],
"leaf" : 0.0
}
uj5u.com熱心網友回復:
在 Scala 中,最好使用基于編譯時反射而不是運行時反射的 Scala JSON 庫。他們可以自動生成編解碼器——只要你堅持 Scala 使用sealed traits(或sealed abstract classes)和case classes 對資料建模的方式。你也不應該使用vars 和nulls。
可以決議您的資料的庫包括(除其他外):Circe、Play JSON、Jsoniter Scala、Json4s。其中前兩個可以這樣使用:
import cats.syntax.functor._ // for Decoder widen
import io.circe.Decoder
import io.circe.generic.extras.Configuration
import io.circe.generic.extras.semiauto._
import io.circe.parser
import play.api.libs.json._
sealed trait Tree extends Product with Serializable {
val nodeid: Int
}
object Tree {
// Circe config for derivation
private implicit val circeConfig: Configuration =
Configuration.default.withSnakeCaseMemberNames
// PlayJson config for derivation
private implicit val playJsonConfig: JsonConfiguration =
JsonConfiguration(JsonNaming.SnakeCase)
final case class Node(
nodeid: Int,
depth: Int,
split: Int,
splitCondition: Float,
yes: Int,
no: Int,
children: List[Tree]
) extends Tree
object Node {
implicit def nodeCirceDecoder: Decoder[Node] =
deriveConfiguredDecoder
implicit def nodePlayJsonDecoder: Reads[Node] =
Json.reads[Node]
}
final case class Leaf(
nodeid: Int,
leaf: Float
) extends Tree
object Leaf {
implicit val leafCirceDecoder: Decoder[Leaf] =
deriveConfiguredDecoder
implicit val leafPlayJsonDecoder: Reads[Leaf] =
Json.reads[Leaf]
}
// combined manually because no discriminator field
implicit val treeCirceDecoder: Decoder[Tree] =
Decoder[Leaf].widen[Tree] or Decoder[Node].widen[Tree]
// normally I'd use orElse on Reads but it's not by-name (lazy)
implicit val treePlayJsonDecoder: Reads[Tree] = Reads[Tree] { json =>
implicitly[Reads[Leaf]].widen[Tree].reads(json) orElse implicitly[Reads[Node]].widen[Tree].reads(json)
}
}
val input = """{ "nodeid": 30, "depth": 6, "split": 65, "split_condition": -9.53674316e-07, "yes": 43, "no": 44, "missing": 43 , "children": [
| { "nodeid": 43, "leaf": 0.000833201397 },
| { "nodeid": 44, "leaf": -0.00145038881 }
|]}""".stripMargin
io.circe.parser.decode[Tree](input)
Json.fromJson[Tree](Json.parse(input))
(斯卡斯蒂示例)
- 使用宏來派生編解碼器(您的情況是特定的,因為您具有帶有子型別且沒有區分欄位的遞回資料結構 - 使用區分欄位,無需手動組合編解碼器)
- 將它們放入伴隨物件中,這樣您就不必將它們匯入范圍
- 運行回傳的決議代碼
Either[SomeError, YourType]
(示例使用這兩個庫來演示如何使用它們,但您只需要選擇一個)。
由于物件是一次構建或完全構建的,因此沒有理由使它們的欄位可變并用空值初始化。
如果您使用如此精確的值,我還建議使用Doubles,甚至更好BigDecimal的 s,而不是s。Float
如果您想以 Java 方式使用 null、vars、注釋和運行時反射 (Jackson),那么最好使用 Java 標記提出這個問題,因為 Scala 開發人員會積極避免這些問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/419714.html
標籤:
上一篇:scala減少一個復雜的結構
