【问题标题】:Marshalling nested custom Objects in Sangria在 Sangria 中编组嵌套的自定义对象
【发布时间】:2018-02-03 18:03:59
【问题描述】:

我有以下输入对象:

val BusinessInputType = InputObjectType[BusinessInput]("BusinessInput", List(
    InputField("userId", StringType),
    InputField("name", StringType),
    InputField("address", OptionInputType(StringType)),
    InputField("phonenumber", OptionInputType(StringType)),
    InputField("email", OptionInputType(StringType)),
    InputField("hours", ListInputType(BusinessHoursInputType))

  ))


 val BusinessHoursInputType = InputObjectType[BusinessHoursInput]("hours",  List(
    InputField("weekDay", IntType),
    InputField("startTime", StringType),
    InputField("endTime", StringType)
  ))

这是我定义了自定义编组的模型:

case class BusinessInput(userId: String, name: String, address: Option[String], phonenumber: Option[String], email: Option[String], hours: Seq[BusinessHoursInput])

object BusinessInput {

  implicit val manual = new FromInput[BusinessInput] {
    val marshaller = CoercedScalaResultMarshaller.default

    def fromResult(node: marshaller.Node) = {
      val ad = node.asInstanceOf[Map[String, Any]]

      System.out.println(ad)
      BusinessInput(
        userId = ad("userId").asInstanceOf[String],
        name = ad("name").asInstanceOf[String],
        address = ad.get("address").flatMap(_.asInstanceOf[Option[String]]),
        phonenumber = ad.get("phonenumber").flatMap(_.asInstanceOf[Option[String]]),
        email = ad.get("email").flatMap(_.asInstanceOf[Option[String]]),
        hours = ad("hours").asInstanceOf[Seq[BusinessHoursInput]]

      )
    }
  }
}



case class BusinessHoursInput(weekDay: Int, startTime: Time, endTime: Time)

object BusinessHoursInput {

  implicit val manual = new FromInput[BusinessHoursInput] {
    val marshaller = CoercedScalaResultMarshaller.default
    def fromResult(node: marshaller.Node) = {
      val ad = node.asInstanceOf[Map[String, Any]]
      System.out.println("HEY")

      BusinessHoursInput(
        weekDay = ad("weekDay").asInstanceOf[Int],
        startTime = Time.valueOf(ad("startTime").asInstanceOf[String]),
        endTime = Time.valueOf(ad("endTime").asInstanceOf[String])
      )
    }
  }


}

我的问题是,当我有一个具有自定义编组的嵌套 InputObject 时,我看不到在编组 BusinessInput 之前调用 BusinessHoursInput 的编组。我注意到这一点是因为“嘿”的打印语句从未在BusinessInput 中的“广告”的打印语句之前执行。当我尝试在数据库中插入BusinessInput 的小时字段时,这会导致我以后遇到问题,因为它无法将其转换为BusinessHoursInput 对象。在 Sangria 中,是否不能在编组父 Object 之前自定义 Marshal 嵌套 Objects?

【问题讨论】:

    标签: scala playframework sangria


    【解决方案1】:

    您可能正在使用BusinessInput 作为参数类型。实际的隐式查找发生在 Argument 定义时间并且仅针对 BusinessInput 类型。

    由于FromInput 是基于类型类的反序列化,您需要显式定义嵌​​套对象的反序列化器之间的依赖关系。例如,您可以像这样重写反序列化器:

    案例类BusinessInput(userId: String, name: String, address: Option[String], phonenumber: Option[String], email: Option[String], hours: Seq[BusinessHoursInput])

    object BusinessInput {
      implicit def manual(implicit hoursFromInput: FromInput[BusinessHoursInput]) = new FromInput[BusinessInput] {
        val marshaller = CoercedScalaResultMarshaller.default
    
        def fromResult(node: marshaller.Node) = {
          val ad = node.asInstanceOf[Map[String, Any]]
    
          BusinessInput(
            userId = ad("userId").asInstanceOf[String],
            name = ad("name").asInstanceOf[String],
            address = ad.get("address").flatMap(_.asInstanceOf[Option[String]]),
            phonenumber = ad.get("phonenumber").flatMap(_.asInstanceOf[Option[String]]),
            email = ad.get("email").flatMap(_.asInstanceOf[Option[String]]),
            hours = hoursFromInput.fromResult(ad("hours").asInstanceOf[Seq[hoursFromInput.marshaller.Node]])
          )
        }
      }
    }
    

    在这个版本中,我利用现有的FromInput[BusinessHoursInput] 从原始输入反序列化BusinessHoursInput

    另外,您可以通过利用现有的基于 JSON 的反序列化器来完全避免手动定义 FromInput 反序列化器。例如,在大多数情况下,circe 的自动推导工作得很好。您只需要这 2 个导入(在定义参数的文件中):

    import sangria.marshalling.circe._
    import io.circe.generic.auto._
    

    这些导入将适当的 FromInput 实例放入范围。这些实例利用了 circe 自己的反序列化机制。

    【讨论】:

    • hours = hoursFromInput.fromResult(ad("hours").asInstanceOf[Seq[hoursFromInput.marshaller.Node]]) 这似乎不起作用。它说,type mismatch, expected hoursFromInput.marshaller.Node but found Seq[hoursFromInput.marshaller.Node]。这很明显,因为 BusinessHoursInput 的 fromResult 只需要节点。我错过了什么?
    • 在完全相同的情况下,嵌套对象没有被反序列化。 @tenshi 任何示例都可以查看嵌套反序列化器的工作原理。谢谢。
    【解决方案2】:
    import io.circe.Decoder
    import io.circe.generic.semiauto.deriveDecoder
    import sangria.macros.derive.deriveInputObjectType
    import sangria.marshalling.circe._
    import sangria.schema.{Argument, InputObjectType}
    
    
    object XXX {
    
      // when you have FromInput for all types in case class (Int, String) you can derive it
      case class BusinessHoursInput(weekDay: Int, startTime: String, endTime: String)
    
      object BusinessHoursInput {
        implicit val decoder: Decoder[BusinessHoursInput] = deriveDecoder
        implicit val inputType: InputObjectType[BusinessHoursInput] = deriveInputObjectType[BusinessHoursInput]()
      }
    
      // the same here, you need InputObjectType also for BusinessHoursInput
      case class BusinessInput(userId: String, name: String, address: Option[String], phonenumber: Option[String], email: Option[String], hours: Seq[BusinessHoursInput])
    
      object BusinessInput {
        implicit val decoder: Decoder[BusinessInput] = deriveDecoder
        implicit val inputType: InputObjectType[BusinessInput] = deriveInputObjectType[BusinessInput]()
      }
    
      // for this to work you need to have in scope InputType BusinessInput and FromInput for BusinessInput
      // FromInput you can get by having Decoder in scope and import sangria.marshalling.circe._
      private val businessInputArg = Argument("businessInput", BusinessInput.inputType)
    
    
    }
    

    如果你不使用 circe 但不同的 json 库,你当然应该有不同的类型类和范围内的正确导入

    【讨论】:

      猜你喜欢
      • 2020-11-21
      • 2021-02-16
      • 2011-01-13
      • 2022-12-18
      • 1970-01-01
      • 2016-05-26
      • 1970-01-01
      • 2016-11-22
      • 1970-01-01
      相关资源
      最近更新 更多