2017-05-11 1 views
0

J'ai créé un fichier DataFrame à partir d'un fichier XML. Le DataFrame créé a le schéma ci-dessous.Champ de masque de colonne (type struct) dans Saprk DataFrame

val df = hiveContext.read.format("com.databricks.spark.xml").option("rowTag", row_tag_name).load(data_dir_path_xml) 

df.printSchema() 

      root 
      |-- samples: struct (nullable = true) 
      | |-- sample: array (nullable = true) 
      | | |-- element: struct (containsNull = true) 
      | | | |-- abc: string (nullable = true) 
      | | | |-- def: long (nullable = true) 
      | | | |-- type: string (nullable = true) 
      |-- abc: string (nullable = true) 

Je voudrais masquer l'abc/def dans la trame de données.

j'ai pu aller sur le terrain que je veux à l'aide:

val abc = df.select($"samples.sample".getField("abc")) 

Mais je veux masquer le champ abc/def (remplacer champ abc avec XXXX) dans la trame de données df. S'il vous plaît aidez-moi sur ce

+0

qu'entendez-vous par masque abc/def? Est-ce que vous voulez masquer abc avec la valeur def? –

+0

Je souhaite remplacer les champs 'abc' et 'def' par une valeur 'xxxxx'. Ces champs sont des données sensibles. – Raj

+0

vous voulez remplacer les valeurs de colonne à droite? –

Répondre

0

Il ne semble pas y avoir beaucoup, voire pas, de soutien dans la bibliothèque xab databricks pour manipuler le contenu d'une base de données XML (ce ne serait pas cool de pouvoir utiliser XSLT ?!). Mais vous pouvez toujours manipuler les lignes inférées directement, par ex.

val abc = df.map(row => { 
    val samples = row.getStruct(0).getSeq(0) 
    val maskedSamples = samples.map(sample => { 
    Row("xxxxx", sample.getLong(1), sample.getString(2)) 
    } 
    Row(Row(maskedSamples), row.getString(1)) 
} 

Le code ci-dessus peut ne pas correspondre précisément à votre transformation désirée, car il est un peu floue, mais vous voyez l'idée.

0

Je vous suggère de diviser le samples arraystructType pour séparer columns (StructFields) de sorte que vous pouvez masquer/les remplacer comme vous voulez. Et vous pouvez également appliquer dataframe functions plus tard si vous le souhaitez.
Ci-dessous le code pour se séparer en trois colonnes

df.withColumn("abcd", lit($"samples.sample.abc")) 
     .withColumn("def", lit($"samples.sample.def")) 
     .withColumn("type", lit($"samples.sample.type")) 

Vous pouvez laisser tomber le samples column si vous voulez

.drop("samples") 

Puisque vous voulez masquer abc et def avec XXXX, vous pouvez le faire

df.withColumn("abcd", lit("XXXX")) 
     .withColumn("def", lit("XXXX")) 
     .withColumn("type", lit($"samples.sample.type")) 
     .drop("samples") 

Remarque: abcd column name est l'utilisation d comme il y a déjà un autre columnabc dans votre schéma

Edité pour répondre aux commentaires @Raj ci-dessous:

Si le original schema doit être préservée et columns ne sont pas nécessaires alors la création d'un case class séparé et une fonction udf devrait faire l'affaire

def mask = udf((typ: mutable.WrappedArray[String]) => Raj("XXXXX", Option(0L), typ(0))) 

Case class pour Raj est nécessaire

case class Raj(abc : String, 
       dfe : Option[Long], 
       typ: String) 

appellent enfin la fonction udf en passant le type dans withColumn

df.withColumn("samples", struct(array(mask(col("samples.sample.type"))) as "sample")) 

Cela devrait vous obtenir la sortie de travail

+0

J'ai essayé d'utiliser df.withColumn. Cela va créer un dataframe avec le schéma racine | - ABCD: string (annulable = true) | - def: longue (annulable = true) | - Type: string (annulable = true) | - abc: string (nullable = true) Mais je veux le même schéma que le df d'origine. – Raj

+0

@Raj, j'ai mis à jour le poste avec la sortie désirée de la vôtre. –