繁体   English   中英

在 Scala 中使用选项 [ 或者 [A,B ] ] 解析 json 的替代方法

[英]Alternative to parsing json with option [ either [A,B ] ] in scala

例如,这里的payload是可选的,它有 3 个变体:

如何使用 option[either[A,B,C]] 之类的类型解析 json,但使用密封特征或总和类型使用抽象数据类型?

下面是一个带有一些样板的最小示例:

https://scalafiddle.io/sf/K6RUWqk/1


// Start writing your ScalaFiddle code here
val json =
  """[
       {
         "id": 1,
         "payload" : "data"
       },
       {
         "id": 2.1,
         "payload" : {
           "field1" : "field1",
           "field2" : 5,
           "field3" : true
         }
       },
      {
         "id": 2.2,
         "payload" : {
           "field1" : "field1",
         }
       },
       {
         "id": 3,
          payload" : 4
       },
       {
         "id":4,
          "
       }
     ]"""

final case class Data(field1: String, field2: Option[Int])
type Payload = Either[String, Data]
final case class Record(id: Int, payload: Option[Payload])

import io.circe.Decoder
import io.circe.generic.semiauto.deriveDecoder

implicit final val dataDecoder: Decoder[Data] = deriveDecoder
implicit final val payloadDecoder: Decoder[Payload] = Decoder[String] either Decoder[Data]
implicit final val recordDecoder: Decoder[Record] = deriveDecoder

val result = io.circe.parser.decode[List[Record]](json)
println(result)

您的代码几乎没问题,您的 json 中只有语法问题,并且Record.id应该是Double而不是Int - 因为这是您的 json ( "id": 2.1 ) 中该字段的显示方式。 请在下面找到固定版本:

val json =
      s"""[
       {
         "id": 1,
         "payload" : "data"
       },
       {
         "id": 2.1,
         "payload" : {
           "field1" : "field1",
           "field2" : 5,
           "field3" : true
         }
       },
       {
         "id": 2.2,
         "payload" : {
           "field1" : "field1"
         }
       },    





       {
         "id": 3,
         "payload" : 4
       },





       {
         "id": 4
       }
     ]"""

    type Payload = Either[String, Data]
    final case class Data(field1: String, field2: Option[Int])
    final case class Record(id: Double, payload: Option[Payload]) // id is a Double in your json in some cases

    import io.circe.Decoder
    import io.circe.generic.semiauto.deriveDecoder

    implicit val dataDecoder: Decoder[Data] = deriveDecoder
    implicit val payloadDecoder: Decoder[Payload] = Decoder[String] either Decoder[Data]
    implicit val recordDecoder: Decoder[Record] = deriveDecoder

    val result = io.circe.parser.decode[List[Record]](json)
    println(result)

在我的情况下产生的:

Right(List(Record(1.0,Some(Left(data))), Record(2.1,Some(Right(Data(field1,Some(5))))), Record(2.2,Some(Right(Data(field1,None)))), Record(3.0,Some(Left(4))), Record(4.0,None)))

更新:

更通用的方法是使用所谓的Sum Types或简单地说 - 具有几种不同实现的通用sealed trait 请参阅下一个 Circe 文档页面以获取更多详细信息: https : //circe.github.io/circe/codecs/adt.html

在你的情况下,它可以实现如下:

import cats.syntax.functor._
  import io.circe.Decoder
  import io.circe.generic.semiauto.deriveDecoder

  sealed trait Payload

  object Payload {
    implicit val decoder: Decoder[Payload] = {
      List[Decoder[Payload]](
        Decoder[StringPayload].widen,
        Decoder[IntPayload].widen,
        Decoder[ObjectPayload].widen
      ).reduce(_ or _)
    }
  }

  case class StringPayload(value: String) extends Payload

  object StringPayload {
    implicit val decoder: Decoder[StringPayload] = Decoder[String].map(StringPayload.apply)
  }

  case class IntPayload(value: Int) extends Payload

  object IntPayload {
    implicit val decoder: Decoder[IntPayload] = Decoder[Int].map(IntPayload.apply)
  }

  case class ObjectPayload(field1: String, field2: Option[Int]) extends Payload

  object ObjectPayload {
    implicit val decoder: Decoder[ObjectPayload] = deriveDecoder
  }

  final case class Record(id: Double, payload: Option[Payload])

  object Record {
    implicit val decoder: Decoder[Record] = deriveDecoder
  }

  def main(args: Array[String]): Unit = {
    val json =
      s"""[
       {
         "id": 1,
         "payload" : "data"
       },
       {
         "id": 2.1,
         "payload" : {
           "field1" : "field1",
           "field2" : 5,
           "field3" : true
         }
       },
       {
         "id": 2.2,
         "payload" : {
           "field1" : "field1"
         }
       },
       {
         "id": 3,
         "payload" : "4"
       },
       {
         "id": 4
       }
     ]"""

    val result = io.circe.parser.decode[List[Record]](json)
    println(result)
  }

在我的情况下产生了下一个输出:

Right(List(Record(1.0,Some(StringPayload(data))), Record(2.1,Some(ObjectPayload(field1,Some(5)))), Record(2.2,Some(ObjectPayload(field1,None))), Record(3.0,Some(StringPayload(4))), Record(4.0,None)))

希望这可以帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM