如何将行映射到protobuf生成的类?

时间:2017-06-26 21:15:51

标签: apache-spark apache-spark-sql protocol-buffers apache-spark-encoders

我需要编写一个读取DataSet [Row]的作业并将其转换为DataSet [CustomClass] CustomClass是protobuf类。

val protoEncoder = Encoders.bean(classOf[CustomClass])
val transformedRows = rows.map {
  case Row(f1: String, f2: Long ) => {
  val pbufClass = CustomClass.newBuilder()
                             .setF1(f1)
                             .setF2(f2)
  pbufClass.build()}}(protoEncoder)

然而,看起来像Protobuf类并不是真正的Java Bean,我确实在下面获得了NPE

val x =  Encoders.bean(classOf[CustomClass])

如何确保作业可以发出类型的数据集 DataSet [CustomClass]其中CustomClass是protobuf类。 有关为类编写自定义编码器的任何指针/示例吗?

NPE:

val encoder2 = Encoders.bean(classOf[CustomClass])
java.lang.NullPointerException
  at org.spark_project.guava.reflect.TypeToken.method(TypeToken.java:465)
  at org.apache.spark.sql.catalyst.JavaTypeInference$$anonfun$2.apply(JavaTypeInference.scala:126)
  at org.apache.spark.sql.catalyst.JavaTypeInference$$anonfun$2.apply(JavaTypeInference.scala:125)
  at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
  at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
  at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
  at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
  at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
  at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:186)
  at org.apache.spark.sql.catalyst.JavaTypeInference$.org$apache$spark$sql$catalyst$JavaTypeInference$$inferDataType(JavaTypeInference.scala:125)
  at org.apache.spark.sql.catalyst.JavaTypeInference$.inferDataType(JavaTypeInference.scala:55)
  at org.apache.spark.sql.catalyst.encoders.ExpressionEncoder$.javaBean(ExpressionEncoder.scala:89)
  at org.apache.spark.sql.Encoders$.bean(Encoders.scala:142)
  ... 48 elided

Bean编码器内部使用

JavaTypeInference.serializerFor(protoClass)

如果我尝试在自定义编码器中执行相同操作,则会收到更具描述性的错误消息:

Caused by: java.lang.UnsupportedOperationException: Cannot infer type for class xxx.yyy.CustomClass because it is not bean-compliant
        at org.apache.spark.sql.catalyst.JavaTypeInference$.org$apache$spark$sql$catalyst$JavaTypeInference$$serializerFor(JavaTypeInference.scala:430)
        at org.apache.spark.sql.catalyst.JavaTypeInference$.serializerFor(JavaTypeInference.scala:337)
        at xxx.yyy..EncoderHolder$.protoEncoder(xxx.scala:69)
        at xxx.yyy..EncoderHolder$.encoder$lzycompute$1(xxx.scala:82)
        at xxx.yyy..EncoderHolder$.encoder$1(xxx.scala:82)
        at xxx.yyy..EncoderHolder$.liftedTree1$1(xxx.scala:84)
        at xxx.yyy..EncoderHolder$.<init>(xxx.scala:81)
        at xxx.yyy..EncoderHolder$.<clinit>(xxx.scala)

5 个答案:

答案 0 :(得分:2)

要将Row转换为Protobuf类,您可以使用sparksql-protobuf

  

该库提供了用于处理Protobuf对象的实用程序   SparkSQL。它提供了一种读取SparkSQL编写的镶木地板文件的方法   返回作为兼容的protobuf对象的RDD。它也可以转换   将protobuf对象的RDD转换为DataFrame。

为您的for (var i = 1; i < 11; i++) { var mydiv="#cc-"+i; $("mydiv iframe").on('hidden.bs.modal', function (e) { $('"#cc-' + i + ' iframe"').attr("src", $('"#cc-' + i + 'iframe"').attr("src")); }); } 文件添加依赖关系

build.sbt

您可以按照库中的一些示例开始

Example 1

Example 2

我希望这有帮助!

答案 1 :(得分:1)

My experience with Encoders are not very promising此时我建议不要花更多的时间在这上面。

我宁愿考虑替代方案以及如何使用Spark的方式,并在最后一步将Spark计算的结果映射到protobuf生成的类。

答案 2 :(得分:0)

虽然不是一个严格的答案,但我确实得到了解决方法。如果我们使用RDD,则不需要编码器。

val rows =
      spark.sql("select * from tablename").as[CaseClass].rdd
val transformedRows = rows.map {
  case Row(f1: String, f2: Long ) => {
  val pbufClass = CustomClass.newBuilder()
                             .setF1(f1)
                             .setF2(f2)
  pbufClass.build()}}

这给了我一个可以使用的Protobuf类的RDD。

答案 3 :(得分:0)

我这样做的方式:我使用了saurfang的sparksql-protobuf库(Github上提供的代码)。您直接获得RDD [ProtoSchema],但很难转换为数据集[ProtoSchema]。我用它来获取信息,主要附加到具有用户定义函数的另一个RDD。

1:导入库

使用Maven:

String value = response.split("TOTAL CREDITS:</B>&NBSP;")[1].split("</FONT>")[0].trim();

2:以RDD [ProtoSchema]

的形式读取数据
<dependencies>
    <dependency>
        <groupId>com.github.saurfang</groupId>
        <artifactId>sparksql-protobuf_2.10</artifactId>
        <version>0.1.2</version>
    </dependency>

    <dependency>
        <groupId>org.apache.parquet</groupId>
        <artifactId>parquet-protobuf</artifactId>
        <version>1.9.0</version>
    </dependency>

    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.5.1</version>
    </dependency>
</dependencies>
...

<repositories>
    <repository>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <id>bintray-saurfang-maven</id>
        <name>bintray</name>
        <url>https://dl.bintray.com/saurfang/maven</url>
    </repository>
</repositories>

(可选)添加PathFilter(Hadoop API)

如果您想添加PathFilter类(就像您以前使用的Hadoop一样),或者激活Hadoop的其他选项,您可以这样做:

val sess: SparkSession = ...
val proto_rdd = new ProtoParquetRDD[ProtoSchema](sess.sparkContext, input_path, classOf[ProtoSchema])

但是,如果您想使用SparkSession阅读其他内容,请不要忘记清除Hadoop配置:

sess.sparkContext.hadoopConfiguration.setBoolean("mapreduce.input.fileinputformat.input.dir.recursive", true)
sess.sparkContext.hadoopConfiguration.setClass("mapreduce.input.pathFilter.class", classOf[MyPathFiltering], classOf[PathFilter])

答案 4 :(得分:0)

默认序列化也不适用于我的protobuf对象。

但是,事实证明内部火花是使用Kryo。所以如果你这样做

private var observer: NSKeyValueObservation?

// ... other code

self.observer = audioSession?.observe(\.outputVolume) { [weak self] (audioSession, _) in
    guard let `self` = self else { return }
    let mute = audioSession.outputVolume
    
    var isMuted = false
    if (mute == 0) && (!self.player.isMuted) {
        isMuted = true
    } else if (mute.isZero) && (self.player.isMuted) {
        isMuted = false
    }
    
    // do what's needed here with `isMuted`
}

有效。