Как преобразовать Dataframe в набор данных, имея ссылку на объект родительского класса в качестве композиции внутри другого класса? - PullRequest
1 голос
/ 29 мая 2020

Я пытаюсь преобразовать Dataframe в Dataset, а структура классов java выглядит следующим образом:

класс A:

public class A {

    private int a;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}

класс B:

public class B extends A {

    private int b;

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }
}

и класс C

public class C {

    private A a;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }
}

, а данные в фрейме данных выглядят следующим образом:

+-----+
|  a  |
+-----+
|[1,2]|
+-----+

Когда я пытаюсь применить кодеры. bean [C] (classOf [C]) в фрейм данных. Ссылка на объект A, которая является экземпляром B в классе C, не возвращает true, когда я проверяю .isInstanceOf [B], я получаю это как false. Вывод набора данных выглядит следующим образом:

+-----+
|  a  |
+-----+
|[1,2]|
+-----+

Как мы получаем все поля A и B под объектом C при итерации по нему в foreach?

Код: -

object TestApp extends App {

  implicit val sparkSession = SparkSession.builder()
    .appName("Test-App")
    .config("spark.sql.codegen.wholeStage", value = false)
    .master("local[1]")
    .getOrCreate()


  var schema = new StructType().
    add("a", new ArrayType(new StructType().add("a", IntegerType, true).add("b", IntegerType, true), true))


  var dd = sparkSession.read.schema(schema).json("Test.txt")

  var ff = dd.as(Encoders.bean[C](classOf[C]))
  ff.show(truncate = false)



  ff.foreach(f => {
    println(f.getA.get(0).isInstanceOf[A])//---true
    println(f.getA.get(0).isInstanceOf[B])//---false
  })

Содержимое файла: {"a":[{"a":1,"b":2}]}

1 Ответ

0 голосов
/ 30 мая 2020

Spark-catalyst использует отражение Google для получения схемы из java beans. Обратите внимание на JavaTypeInference.scala # inferDataType . Этот класс использует геттеры для сбора имени поля и returnType геттеров для вычисления SparkType.

Поскольку класс C имеет геттер с именем getA() с returnType как A и A, в Turn, имеет геттер как getA() с returnType как int, схема будет создана как struct<a:struct<a:int>>, где struct<a:int> получено из getA класса A.

Решение этого проблема, о которой я могу думать, -

// Modify your class C to have Real class reference rather its super type
public class C {

    private B a;

    public B getA() {
        return a;
    }

    public void setA(B a) {
        this.a = a;
    }
}

Вывод -

root
 |-- a: struct (nullable = true)
 |    |-- a: integer (nullable = false)
 |    |-- b: integer (nullable = false)

+------+
|a     |
+------+
|[1, 2]|
+------+
...