任何可以消除我的 Spark 模式消除器中的 asInstanceOf 和 A​​ny 的 scala 技巧?

any scala trick that would eliminate asInstanceOf and Any in my Spark schema de-nullifier?

这并不是一个真正的 Spark 问题,而是一个与 Scala 类型相关的问题,但我正在做的事情可能会引起 Spark 粉丝的兴趣,所以我将 'Spark' 保留在我的框架中的问题,即:

I want to recursively transform a spark sql schema of StructType, which contains a list whose elements may be either StructType's or StructField's. The result of the transform should be a version of the original schema which disallows nulls in any field. Unfortunately, StructType and StructField don't extend from a common marker trait. This lead to my initial implementation where the method accepted 'Any' and explicitly cast the result back to StructType.

初步实施

object SchemaTools extends App  {
  import org.apache.spark.sql.types._

  def noNullSchema(schema: StructType): StructType = {
    def go(element: Any): Product = element match {
      case x: StructField => x.copy(nullable = false)
      case x: StructType => StructType(x.fields.map(_.copy(nullable = false)))
      case bad => sys.error(s"element of unexpected type: $bad")
    }

    go(schema).asInstanceOf[StructType]
  }

  type Rec = (String, Seq[(Int, Int, String)])
  val schema: StructType = Encoders.product[Rec].schema

  System.out.println("pr:" + schema.prettyJson)
  System.out.println("pr:" + noNullSchema(schema).prettyJson)
}

更新

我接受 Tim 的回答,因为他友好地指出了我的愚蠢错误,即我没有递归到嵌套结构中。我在下面包含了上述 "proof of concept" 的修改版本。这适用于我的示例输入,并说明了我将采用的一般方法。通过此实现,我没有与类型相关的问题。我的错! :我误解了 StructType 中的内容(它始终是 StructField 的数组,而不是 StructField 或 StructType 的数组)。数组中的字段本身可能是 "StructType" 数据类型,这推动了递归的需要。无论如何......下面是一个修改后的 "toy" 实现,它说明了如果我需要一个完整的解决方案(而不是仅仅为了学习而实现),我将如何解决这个问题。此代码绝对不是生产就绪的,并且会在更复杂的输入上失败。不过,它说明了一种可能的方法。

注意:关于 null 和模式,我了解到的另一件事是非常重要的,请牢记......即使正确实现了模式 "de-nuller" Spark 也不会在解析期间强制执行可空性检查。此处对此进行了更详细的讨论:

*概念证明......不再有类型问题*

object SchemaTools extends App  {
  import org.apache.spark.sql.types._

  def noNullSchema(field: StructField): StructField = {
    field.dataType match {
      case  ArrayType(StructType(fields), containsNull) =>
       StructField(
         field.name,
         ArrayType(noNullSchema(StructType(fields)), containsNull),
         nullable = false,
         field.metadata)
      case _ => field.copy(nullable = false)
    }
  }

  def noNullSchema(schema: StructType): StructType =
    StructType (
      schema.fields.map { f =>
        System.out.println("f:" + f);
        noNullSchema(f)
      }
    )

  type Rec = (String, Seq[(Int, String, String)])
  val schema: StructType = Encoders.product[Rec].schema

  System.out.println("pr:" + schema.prettyJson)
  System.out.println("pr:" + noNullSchema(schema).prettyJson)
}

除非我误解了这个问题,否则我认为你只需要调整你原来的解决方案就可以改变

go(schema).asInstanceOf[StructType]

进入

StructType(schema.fields.map(go))

此外,go 的参数和结果类型应与 StructType.fields 的元素类型相同。

由于框架对 fields 的元素使用通用类型,因此必须有一些代码来处理该通用类型。因此,如果该类型是 Any,那么您必须处理 Any,而类型类将无济于事。