使用DataTable.WriteXml()生成的XML文件无法读取数据问题排查
问题描述
我创建了一个包含以下列的DataTable:
dataTable.Columns.Add("ID", typeof(string)); dataTable.Columns.Add("File Name", typeof(string)); dataTable.Columns.Add("Date Taken", typeof(string)); dataTable.Columns.Add("Size", typeof(string)); dataTable.Columns.Add("Unique", typeof(bool));
我用以下代码将DataTable写入XML文件:
if (dgView.DataSource is DataTable dataTable) // 确保数据源是DataTable { if (string.IsNullOrWhiteSpace(dataTable.TableName)) { dataTable.TableName = "MyDataTable"; // 赋值表名避免序列化错误 } string dataTime = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); // 动态时间戳 string defaultFolder = txtSort.Text.Trim(); // 从txtSort获取默认文件夹 string defaultFileName = $"{dataTime}.xml"; using (SaveFileDialog saveFileDialog = new SaveFileDialog()) { saveFileDialog.InitialDirectory = Directory.Exists(defaultFolder) ? defaultFolder : Environment.GetFolderPath(Environment.SpecialFolder.Desktop); saveFileDialog.FileName = defaultFileName; saveFileDialog.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*"; saveFileDialog.Title = "将DataTable保存为XML"; if (saveFileDialog.ShowDialog() == DialogResult.OK) { try { dataTable.WriteXml(saveFileDialog.FileName); MessageBox.Show($"数据已保存到 {saveFileDialog.FileName}", "保存成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show($"保存数据出错: {ex.Message}", "保存错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } else { MessageBox.Show("DataGridView中未找到有效的DataTable。", "无数据", MessageBoxButtons.OK, MessageBoxIcon.Warning); }
生成的XML文件内容如下:
<?xml version="1.0" standalone="yes"?> <DocumentElement> <MyDataTable> <ID>1</ID> <File_x0020_Name>H:\Image backup\TEST_SOURCE\01\IMG_7718(8).jpg</File_x0020_Name> <Date_x0020_Taken>Jan/17/2016 14:46:06</Date_x0020_Taken> <Size>0.673 MB</Size> <Unique>true</Unique> </MyDataTable> <MyDataTable> <ID>2</ID> <File_x0020_Name>H:\Image backup\TEST_SOURCE\01\IMG_7718(2).jpg</File_x0020_Name> <Date_x0020_Taken>Jan/17/2016 14:46:06</Date_x0020_Taken> <Size>0.673 MB</Size> <Unique>true</Unique> </MyDataTable> <MyDataTable> <ID>3</ID> <File_x0020_Name>H:\Image backup\TEST_SOURCE\01\IMG_7719(3).jpg</File_x0020_Name> <Date_x0020_Taken>Jan/17/2016 14:46:07</Date_x0020_Taken> <Size>1.045 MB</Size> <Unique>true</Unique> </MyDataTable> <MyDataTable> <ID>7</ID> <File_x0020_Name>H:\Image backup\TEST_SOURCE\01\IMG_7719(2).jpg</File_x0020_Name> <Date_x0020_Taken>Jan/17/2016 14:46:07</Date_x0020_Taken> <Size>1.045 MB</Size> <Unique>true</Unique> </MyDataTable> </DocumentElement>
但我用以下代码读取该文件时,没有加载到任何数据(行数为0):
private void btnRetrieve_Click(object sender, EventArgs e) { OpenFileDialog OpenFileDialog = new OpenFileDialog(); if (OpenFileDialog.ShowDialog() == DialogResult.OK) { string fileName = OpenFileDialog.FileName; Task.Run(() => { // 将XML中的数据加载到DataTable DataTable dataTable = new DataTable(); dataTable.Columns.Add("ID", typeof(string)); dataTable.Columns.Add("File_x0020_Name", typeof(string)); // 处理空格为_x0020_ dataTable.Columns.Add("Date_x0020_Taken", typeof(string)); // 处理空格为_x0020_ dataTable.Columns.Add("Size", typeof(string)); dataTable.Columns.Add("Unique", typeof(bool)); // 读取XML到DataTable if (File.Exists(fileName)) { try { dataTable.ReadXml(fileName); // 检查是否加载到数据 if (dataTable.Rows.Count == 0) { this.Invoke(new Action(() => { MessageBox.Show("XML文件已加载,但未找到数据。", "数据加载错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); })); } else { // 用DataTable中的数据填充字典 ConcurrentDictionary<string, List<mediaFile>> g_allFiles = new ConcurrentDictionary<string, List<mediaFile>>(); foreach (DataRow row in dataTable.Rows) { mediaFile media = new mediaFile { id = row["ID"].ToString(), fileName = row["File_x0020_Name"].ToString(), exif_date = row["Date_x0020_Taken"].ToString(), fileSize = row["Size"].ToString(), unique = Convert.ToBoolean(row["Unique"]) }; // 提取文件扩展名(3个字符) string fileExtension = Path.GetExtension(media.fileName)?.Substring(1, 3).ToLower(); if (!string.IsNullOrEmpty(fileExtension)) { if (!g_allFiles.ContainsKey(fileExtension)) { g_allFiles[fileExtension] = new List<mediaFile>(); } g_allFiles[fileExtension].Add(media); } } dataTable.Columns["File_x0020_Name"].ColumnName = "File Name"; dataTable.Columns["Date_x0020_Taken"].ColumnName = "Date Taken"; // 更新DataGridView显示加载的数据 this.Invoke(new Action(() => { dgView.DataSource = dataTable; })); } } catch (Exception ex) { // 处理读取XML时的错误 this.Invoke(new Action(() => { MessageBox.Show($"读取XML文件出错: {ex.Message}", "文件读取错误", MessageBoxButtons.OK, MessageBoxIcon.Error); })); } } else { // 如果文件不存在,在UI线程显示提示 this.Invoke(new Action(() => { MessageBox.Show("指定的文件路径不存在。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); })); } }); } var k = g_allFiles; }
请问我哪里操作出错了?
解决方法
问题核心在于读取XML时创建的DataTable未设置与写入时一致的TableName:你写入XML时给DataTable指定了MyDataTable的表名,XML中的数据都包裹在<MyDataTable>节点下;但读取时新建的DataTable用的是默认表名(如Table),ReadXml会默认寻找与当前DataTable表名匹配的节点,两者不匹配导致数据无法加载。
另外还有一个编译错误:你在Task.Run外部访问了内部定义的g_allFiles变量,局部变量作用域不允许这种操作。
修正后的读取代码
private void btnRetrieve_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); if (openFileDialog.ShowDialog() == DialogResult.OK) { string fileName = openFileDialog.FileName; Task.Run(() => { // 关键:设置和写入时一致的表名 DataTable dataTable = new DataTable("MyDataTable"); dataTable.Columns.Add("ID", typeof(string)); dataTable.Columns.Add("File_x0020_Name", typeof(string)); dataTable.Columns.Add("Date_x0020_Taken", typeof(string)); dataTable.Columns.Add("Size", typeof(string)); dataTable.Columns.Add("Unique", typeof(bool)); // 读取XML到DataTable if (File.Exists(fileName)) { try { dataTable.ReadXml(fileName); // 检查是否加载到数据 if (dataTable.Rows.Count == 0) { this.Invoke(new Action(() => { MessageBox.Show("XML文件已加载,但未找到数据。", "数据加载错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); })); } else { // 用DataTable中的数据填充字典 ConcurrentDictionary<string, List<mediaFile>> g_allFiles = new ConcurrentDictionary<string, List<mediaFile>>(); foreach (DataRow row in dataTable.Rows) { mediaFile media = new mediaFile { id = row["ID"].ToString(), fileName = row["File_x0020_Name"].ToString(), exif_date = row["Date_x0020_Taken"].ToString(), fileSize = row["Size"].ToString(), unique = Convert.ToBoolean(row["Unique"]) }; // 提取文件扩展名(3个字符) string fileExtension = Path.GetExtension(media.fileName)?.Substring(1, 3).ToLower(); if (!string.IsNullOrEmpty(fileExtension)) { if (!g_allFiles.ContainsKey(fileExtension)) { g_allFiles[fileExtension] = new List<mediaFile>(); } g_allFiles[fileExtension].Add(media); } } dataTable.Columns["File_x0020_Name"].ColumnName = "File Name"; dataTable.Columns["Date_x0020_Taken"].ColumnName = "Date Taken"; // 更新DataGridView显示加载的数据 this.Invoke(new Action(() => { dgView.DataSource = dataTable; })); // 若需外部使用g_allFiles,需将其改为类成员变量,或通过回调传递 // 示例:this.g_allFiles = g_allFiles; } } catch (Exception ex) { // 处理读取XML时的错误 this.Invoke(new Action(() => { MessageBox.Show($"读取XML文件出错: {ex.Message}", "文件读取错误", MessageBoxButtons.OK, MessageBoxIcon.Error); })); } } else { // 如果文件不存在,在UI线程显示提示 this.Invoke(new Action(() => { MessageBox.Show("指定的文件路径不存在。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); })); } }); } // 移除无效的var k = g_allFiles; 局部变量作用域限制无法访问 }
额外说明
- 读取时的DataTable表名必须和写入时完全一致,
ReadXml才能正确识别XML中的数据节点。 - 若不想手动设置表名,也可先调用
dataTable.ReadXmlSchema()读取XML结构(需写入时用WriteXmlSchema保存Schema),再读取数据。 - 局部变量
g_allFiles无法跨作用域访问,需改为类成员变量或通过其他方式传递到外部。
内容的提问来源于stack exchange,提问作者PCG
相关产品推荐
相关产品推荐

