Pyspark 如何更正model.json的列数大于输出的CSV文件

Pyspark 如何更正model.json的列数大于输出的CSV文件,pyspark,dynamics-crm,azure-data-lake,azure-databricks,common-data-service,Pyspark,Dynamics Crm,Azure Data Lake,Azure Databricks,Common Data Service,我正在尝试以CDM格式从Azure数据湖(gen2)中的CSV文件创建数据帧。文件定义位于顶层的model.json文件中;该文件描述了数据湖中的每个实体。此数据由输出 我的目标是读取此文件并在Azure Databricks中进行一些处理。我可以成功读取model.json文件并提取每个实体的列名,但我遇到了某些CSV文件,这些文件的列数少于model.json文件中描述的列数,您可以想象,尝试将这些列名应用于非标题CSV文件将导致错误: java.lang.IllegalArgumentEx

我正在尝试以CDM格式从Azure数据湖(gen2)中的CSV文件创建数据帧。文件定义位于顶层的model.json文件中;该文件描述了数据湖中的每个实体。此数据由输出

我的目标是读取此文件并在Azure Databricks中进行一些处理。我可以成功读取model.json文件并提取每个实体的列名,但我遇到了某些CSV文件,这些文件的列数少于model.json文件中描述的列数,您可以想象,尝试将这些列名应用于非标题CSV文件将导致错误:

java.lang.IllegalArgumentException: requirement failed: The number of columns doesn't match.
下面是一些描述转换过程的代码片段。感谢您的帮助。如果有一个更简单的方法来处理CSV文件中的数据,那么我也很有兴趣听到这个

加载model.json文件

model = spark.read.json(base_path + "model.json", multiLine=True)
entities = model.select(explode(model["entities"]).alias("entity"))
entity_info = entities.select("entity.name", "entity.attributes", "entity.partitions")
从JSON文件中提取列名和文件路径

entity_metadata = (
  filtered_entity_info.withColumn("attributes", explode("attributes"))
  .select("name", "partitions", col("attributes")["name"].alias("column_name"))
)

entity_metadata = (
  entity_metadata.groupBy("name", "partitions")
  .agg(collect_list("column_name").alias("columns"))
  .select("*")
)

entity_metadata = (
  entity_metadata.withColumn("partitions", explode("partitions"))
  .select("name", col("partitions")["location"].alias("filePath"), "columns")
)
加载文件,应用列名以尝试创建DF

def build_file_url(file_url):
  url = file_url.split(blob_container_name + "/")[1]
  return base_path + url
  
  
def populate_entity_df(tableName, url, column_names):
  file_path = build_file_url(url)
  df = (
    spark.read.option("header", "false")
    .option("inferSchema", "true")
    .option("delimiter", ',')
    .option("dateFormat", "yyyy-MM-dd'T'HH:mm:ss'Z'")
    .option("multiLine", "true")
    .csv(file_path)
  )
  return df.toDF(*column_names)

array_of_metadatas = entity_metadata.collect()

opportunity_metadata = next(x for x in array_of_metadatas if x.name == "opportunity")

opportunity_df = populate_entity_df(opportunity_metadata.name, opportunity_metadata.filePath, opportunity_metadata.columns)
如果有兴趣,这里是model.json文件的一个示例

{
“名称”:“cdm”,
“说明”:“清洁发展机制”,
“版本”:“1.0”,
“实体”:[
{
“$type”:“LocalEntity”,
“名称”:“账户”,
“说明”:“账户”,
“注释”:[
{
“名称”:“雅典娜:分区粒度”,
“价值”:“年”
},
{
“姓名”:“雅典娜:初始状态”,
“值”:“已完成”
},
{
“名称”:“雅典娜:InitialSyncDataCompletedTime”,
“价值”:“2020年9月1日下午3:43:50”
}
],
“属性”:[
{
“名称”:“Id”,
“数据类型”:“guid”
},
{
“名称”:“SinkCreatedOn”,
“数据类型”:“日期时间”
},
{
“姓名”:“Sinkdon”,
“数据类型”:“日期时间”
},
{
“名称”:“州代码”,
“数据类型”:“int64”
},
{
“名称”:“状态代码”,
“数据类型”:“int64”
},
...
],
“分区”:[
{
“名称”:“2020年”,
“位置”:https://.dfs.core.windows.net:443//opportunity/Snapshot/2020_1602009522.csv",
“文件格式设置”:{
“$type”:“CsvFormatSettings”,
“columnHeaders”:false,
“分隔符”:“,”,
“quoteStyle”:“quoteStyle.Csv”,
“csvStyle”:“csvStyle.QuoteAlways”,
“编码”:“UTF-8”
},
“注释”:[
{
“姓名”:“雅典娜:分区年”,
“价值”:“2020年”
}
]
}
]
}
]
}

结果表明,输出的CSV文件的经典问题是每列都没有逗号。我没有注意到这一点,因为Dynamics 365实体有数百列,在查看文件时,看到的是387个逗号而不是378个逗号,所以没有完全注册

jim,12,
bob,13,programmer,texas,houston
jane,88,director,alaska
PySpark在使用.csv api时,只使用第一行的列数,并从以后的行中删除任何额外的列

为了解决这个问题,我使用列名列表在运行时生成一个模式

def get_模式(cols):
arr=[]
对于col中的col:
arr.append(StructField(col,StringType(),True))
返回结构类型(arr)
我现在只使用StringType,但将来从实体定义中引入数据类型并创建映射似乎很容易

为了将其结合在一起,以下是模式的应用方式:

df=(
spark.read.option(“标题”、“假”)
.schema(schema)
.option(“分隔符”、“,”)
.选项(“日期格式”,“yyyy-MM-dd'T'HH:MM:ss'Z'”)
.选项(“多行”、“真”)
.csv(文件路径)
)