组合类中的多态 lift-json 反序列化

发布于 2024-12-06 00:59:12 字数 788 浏览 3 评论 0原文

我正在尝试使用 Lift-Json 自动将 json 对象反序列化为 scala 类,其中的坐标类用于存储 GeoJson 信息。

case class Request(name:String, geometry:Geometry)

sealed abstract class Geometry

case class Point(coordinates:(Double,Double)) extends Geometry
case class LineString(coordinates:List[Point]) extends Geometry
case class Polygon(coordinates:List[LineString]) extends Geometry

我想反序列化一个 json 字符串,如下所示:

{
name:"test",
geometry:{
   "type": "LineString",
   "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
  }
}

转换为 Request 案例类,并在 Geometry 字段中使用正确的 LineString 运行时类。我想我应该使用 TypeHint 但如何呢?这是正确的方法还是我应该创建三个不同的请求(RequestPoint、RequestLineString 和 RequestPolygon)? 这将是要反序列化的 Scala 代码:

val json = parse(message)
json.extract[Request]

I am trying to automatically deserialize json object to a scala class using Lift-Json with a coordinate class inside used to store GeoJson information.

case class Request(name:String, geometry:Geometry)

sealed abstract class Geometry

case class Point(coordinates:(Double,Double)) extends Geometry
case class LineString(coordinates:List[Point]) extends Geometry
case class Polygon(coordinates:List[LineString]) extends Geometry

I want to deserialize a json string like this:

{
name:"test",
geometry:{
   "type": "LineString",
   "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
  }
}

into a Request case class with the right LineString runtime class in the Geometry field. I guess I should use a TypeHint but how?. Is this the correct approach or should I create three different Request (RequestPoint,RequestLineString and RequestPolygon)?
This would be the Scala code to deserialize:

val json = parse(message)
json.extract[Request]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不顾 2024-12-13 00:59:12

是的,您需要对几何类型等求和类型使用类型提示。这是一个例子:

implicit val formats = DefaultFormats.withHints(ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon])))

val r = Request("test", LineString(List(Point(100.0, 0.0), Point(101.0, 1.0))))

Serialization.write(r)

{
"name":"test",
"geometry":{
  "jsonClass":"LineString",
  "coordinates":[{"jsonClass":"Point","coordinates":{"_1$mcD$sp":100.0,"_2$mcD$sp":0.0}},{"jsonClass":"Point","coordinates":{"_1$mcD$sp":101.0,"_2$mcD$sp":1.0}}]}
}

不完全是你想要的。由于您想要更改 Points 的默认序列化方案,因此需要为该类型提供自定义序列化程序。

class PointSerializer extends Serializer[Point] {
  private val Class = classOf[Point]

  def deserialize(implicit format: Formats) = {
    case (TypeInfo(Class, _), json) => json match {
      case JArray(JDouble(x) :: JDouble(y) :: Nil) => Point(x, y)
      case x => throw new MappingException("Can't convert " + x + " to Point")
    }
  }

  def serialize(implicit format: Formats) = {
    case p: Point => JArray(JDouble(p.coordinates._1) :: JDouble(p.coordinates._2) :: Nil)
  }
}

// Configure it
implicit val formats = DefaultFormats.withHints(ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon]))) + new PointSerializer

Serialization.write(r)

{
  "name":"test",
  "geometry":{
    "jsonClass":"LineString",
    "coordinates":[[100.0,0.0],[101.0,1.0]]
  }
}

更好,但如果您需要将名为“jsonClass”的默认字段更改为“type”,则需要再进行一项配置:

implicit val formats = new DefaultFormats { 
  override val typeHintFieldName = "type" 
  override val typeHints = ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon]))
} + new PointSerializer

Serialization.write(r)

{
  "name":"test",
  "geometry":{
    "type":"LineString",
    "coordinates":[[100.0,0.0],[101.0,1.0]]
  }
}

Yes, you need to use type hints for sum types like Geometry. Here's one example:

implicit val formats = DefaultFormats.withHints(ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon])))

val r = Request("test", LineString(List(Point(100.0, 0.0), Point(101.0, 1.0))))

Serialization.write(r)

{
"name":"test",
"geometry":{
  "jsonClass":"LineString",
  "coordinates":[{"jsonClass":"Point","coordinates":{"_1$mcD$sp":100.0,"_2$mcD$sp":0.0}},{"jsonClass":"Point","coordinates":{"_1$mcD$sp":101.0,"_2$mcD$sp":1.0}}]}
}

Not quite what you wanted. Since you want to change the default serialization scheme for Points, you need to provide a custom serializer for that type.

class PointSerializer extends Serializer[Point] {
  private val Class = classOf[Point]

  def deserialize(implicit format: Formats) = {
    case (TypeInfo(Class, _), json) => json match {
      case JArray(JDouble(x) :: JDouble(y) :: Nil) => Point(x, y)
      case x => throw new MappingException("Can't convert " + x + " to Point")
    }
  }

  def serialize(implicit format: Formats) = {
    case p: Point => JArray(JDouble(p.coordinates._1) :: JDouble(p.coordinates._2) :: Nil)
  }
}

// Configure it
implicit val formats = DefaultFormats.withHints(ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon]))) + new PointSerializer

Serialization.write(r)

{
  "name":"test",
  "geometry":{
    "jsonClass":"LineString",
    "coordinates":[[100.0,0.0],[101.0,1.0]]
  }
}

Better, but you need one more configuration if you need to change the default field named as 'jsonClass' to 'type':

implicit val formats = new DefaultFormats { 
  override val typeHintFieldName = "type" 
  override val typeHints = ShortTypeHints(List(classOf[Point], classOf[LineString], classOf[Polygon]))
} + new PointSerializer

Serialization.write(r)

{
  "name":"test",
  "geometry":{
    "type":"LineString",
    "coordinates":[[100.0,0.0],[101.0,1.0]]
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文