Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用spray Json对Scala case对象进行Json反序列化_Json_Scala_Spray Json - Fatal编程技术网

使用spray Json对Scala case对象进行Json反序列化

使用spray Json对Scala case对象进行Json反序列化,json,scala,spray-json,Json,Scala,Spray Json,我正在尝试为以下域模型使用spray-json编写一个自定义JsonReader: sealed trait OrderType object OrderType { case object MARKET extends OrderType case object LIMIT extends OrderType case object STOP extends OrderType case object MARKET_IF_TOUCHED extends OrderType

我正在尝试为以下域模型使用
spray-json
编写一个自定义
JsonReader

sealed trait OrderType
object OrderType {
  case object MARKET extends OrderType
  case object LIMIT extends OrderType
  case object STOP extends OrderType
  case object MARKET_IF_TOUCHED extends OrderType
  case object TAKE_PROFIT extends OrderType
  case object STOP_LOSS extends OrderType
  case object TRAILING_STOP_LOSS extends OrderType
}
以下是我为此创建的自定义
JsonReader

implicit object OrderTypeJsonReader extends JsonReader[OrderType] {
  def read(value: JsValue): OrderType = value match {
    case JsString("MARKET") => MARKET
    case JsString("LIMIT") => LIMIT
    case JsString("STOP") => STOP
    case JsString("MARKET_IF_TOUCHED") => MARKET_IF_TOUCHED
    case JsString("TAKE_PROFIT") => TAKE_PROFIT
    case JsString("STOP_LOSS") => STOP_LOSS
    case JsString("TRAILING_STOP_LOSS") => TRAILING_STOP_LOSS
    case _ => deserializationError("OrderType expected")
  }
}

考虑到json字符串和
案例对象的名称相同,这里有没有避免代码重复的方法?

您可以尝试用部分函数(或映射)替换模式匹配:

缺点是必须手动指定所有案例对象的列表。您可以尝试使用反射来生成它。 也许这个问题会有帮助。然后你可以有:

import scala.reflect.runtime.universe

private val tpe = universe.typeOf[OrderType]
private val clazz = tpe.typeSymbol.asClass

private def objectBy[T](name: String): T = Class.forName(OrderType.getClass.getName + name + "$").newInstance().asInstanceOf[T]

val string2orderType: Map[JsValue, OrderType] = clazz.knownDirectSubclasses.map { sc =>
  val objectName = sc.toString.stripPrefix("object ")
  (JsString(objectName), objectBy[OrderType](objectName))
}.toMap

implicit object OrderTypeJsonReader extends JsonReader[OrderType] {
  def read(value: JsValue): OrderType = string2orderType.getOrElse(value, deserializationError("OrderType expected"))
}
另请参阅有关向Spray添加默认案例类格式的讨论:

更新以处理评论

是否有可能将其“泛化”为任何类型的T?我有很多密封的trait/case对象枚举,希望将样板文件保持在最小值

我想到了这个:

import spray.json._
import Utils._

sealed trait OrderStatus
object OrderStatus {
  case object Cancelled extends OrderStatus
  case object Delivered extends OrderStatus
  // More objects...

  implicit object OrderStatusJsonReader extends ObjectJsonReader[OrderStatus]
}

sealed trait OrderType
object OrderType {
  case object MARKET extends OrderType
  case object LIMIT extends OrderType
  // More objects...

  implicit object OrderTypeJsonReader extends ObjectJsonReader[OrderType]
}

object Utils {
  import scala.reflect.ClassTag
  import scala.reflect.runtime.universe._

  def objectBy[T: ClassTag](name: String): T = {
    val c = implicitly[ClassTag[T]]
    Class.forName(c + "$" + name + "$").newInstance().asInstanceOf[T]
  }

  def string2trait[T: TypeTag : ClassTag]: Map[JsValue, T] = {
    val clazz = typeOf[T].typeSymbol.asClass
    clazz.knownDirectSubclasses.map { sc =>
      val objectName = sc.toString.stripPrefix("object ")
      (JsString(objectName), objectBy[T](objectName))
    }.toMap
  }

  class ObjectJsonReader[T: TypeTag : ClassTag] extends JsonReader[T] {
    val string2T: Map[JsValue, T] = string2trait[T]
    def defaultValue: T = deserializationError(s"${ implicitly[ClassTag[T]].runtimeClass.getCanonicalName } expected")
    override def read(json: JsValue): T = string2T.getOrElse(json, defaultValue)
  }
}
然后你可以像这样使用它:

import OrderType._
import OrderStatus._
JsString("MARKET").convertTo[OrderType]
JsString(OrderStatus.Cancelled.toString).convertTo[OrderStatus]
我还尝试了github问题中的代码,可以这样使用:

implicit val orderTypeJsonFormat: RootJsonFormat[OrderType] = 
  caseObjectJsonFormat(MARKET, LIMIT, STOP, MARKET_IF_TOUCHED, TAKE_PROFIT, STOP_LOSS, TRAILING_STOP_LOSS)

不幸的是,这需要显式指定所有对象。如果你想这样做,那么,我想,我的第一个建议(没有思考)更好。(因为它没有反射:-)

谢谢你的回答,我喜欢反射代码来生成从json字符串到对象的映射。是否可以将其“泛化”为任何类型的
T
?我有很多密封的trait/case对象枚举,希望将样板文件保持在最小值。顺便说一句,我也尝试了来自的代码,它可以这样使用:
implicit val orderTypeJsonFormat:RootJsonFormat[OrderType]=caseObjectJsonFormat(市场、限制、止损、市场、获利、止损、拖尾止损)
@msilb抱歉,我无法简洁地表达,这就是为什么必须更新答案的原因。谢谢,我测试过了,效果很好。最后,我决定完全放弃使用json,主要是因为我不想在我的公共图书馆里有任何这种相当脆弱的“反射黑魔法”,而是转而使用,这似乎做得更好,因为它依赖于为
案例类
es和
案例对象
s自动派生
解码器
s。对于我的用例,它或多或少是“开箱即用”的。
implicit val orderTypeJsonFormat: RootJsonFormat[OrderType] = 
  caseObjectJsonFormat(MARKET, LIMIT, STOP, MARKET_IF_TOUCHED, TAKE_PROFIT, STOP_LOSS, TRAILING_STOP_LOSS)