Class 使用 XmlReader 未按预期抓取 XML 数据
Class using XmlReader is not grabbing XML data as expected
我的应用程序中有一个 class 没有按预期转换我的 XML 数据。
XML数据
以下是 XML 的节选。文件大小介于 2 GB 和 3 GB 之间,数据代表共同基金。每个基金通常都有与之关联的经理,但也有可能列出 none 名。数据中的 Fund 可以有多个 ManagerDetail 节点,也可以没有 ManagerDetail 节点。每个经理可以有多个 CollegeEducation 节点或没有 CollegeEducation 节点。
<MutualFund>
<ManagerList>
<ManagerDetail>
<ManagerRole>M</ManagerRole>
<ManagerId>7394</ManagerId>
<ManagerTenure>3.67</ManagerTenure>
<StartDate>2011-09-30</StartDate>
<OwnershipLevel>6</OwnershipLevel>
<GivenName>Stephen</GivenName>
<MiddleName>M.</MiddleName>
<FamilyName>Kane</FamilyName>
<Gender>M</Gender>
<Certifications>
<CertificationName>CFA</CertificationName>
</Certifications>
<CollegeEducations>
<CollegeEducation>
<School>University of Chicago</School>
<Year>1990</Year>
<Degree>M.B.A.</Degree>
</CollegeEducation>
<CollegeEducation>
<School>University of California - Berkeley</School>
<Year>1985</Year>
<Degree>B.S.</Degree>
<Major>Business</Major>
</CollegeEducation>
</CollegeEducations>
</ManagerDetail>
</ManagerList>
</MutualFund>
C#Class
我创建了一个 class,它以另一种形式在 BackgroundWorker 实例中调用。这个class把上面的数据放到下面table:
public static DataTable dtManagersEducation = new DataTable();
dtManagersEducation.Columns.Add("ManagerId");
dtManagersEducation.Columns.Add("Institution");
dtManagersEducation.Columns.Add("DegreeType");
dtManagersEducation.Columns.Add("Emphasis");
dtManagersEducation.Columns.Add("Year");
放置XML数据的方法是这样设置的。基本上,我有一些创建和完成数据行的点,并且某些 XML 数据将在读取数据时放入可用行中。
public static void Read(MainForm mf, XmlReader xml)
{
mainForm = mf;
xmlReader = xml;
while (xmlReader.Read() && mainForm.continueProcess)
{
if (xmlReader.Name == "CollegeEducation")
{
if (nodeIsElement())
{
drManagersEducation = dtManagersEducation.NewRow();
drManagersEducation["ManagerId"] = currentManager.morningstarManagerId;
}
else if (nodeIsEndElement())
{
dtManagersEducation.Rows.Add(drManagersEducation);
drManagersEducation = null;
}
}
else if (xmlReader.Name == "School")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["Institution"] = value;
}
}
else if (xmlReader.Name == "Year")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["Year"] = value;
}
}
else if (xmlReader.Name == "Degree")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["DegreeType"] = value;
}
}
else if (xmlReader.Name == "Major")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["Emphasis"] = value;
}
}
}
}
private static bool nodeIsElement()
{
return xmlReader.NodeType == XmlNodeType.Element;
}
private static bool nodeIsEndElement()
{
return xmlReader.NodeType == XmlNodeType.EndElement;
}
结果最终在 Emphasis 或 Year 列中没有数据,正如您在上面看到的,有实例(大量)在这些字段中有数据。
ManagerId Institution DegreeType Emphasis Year
5807 Yale University M.S.
9336 Yale University
7227 Yale University M.S.
你们是否碰巧对正在发生的事情有所了解?
谢谢
编辑:回答
我上面列出的示例 XML 数据有缩进空格,但我通过 XmlReader 运行 得到的实际数据没有。如 dbc 所示,添加变量 bool readNext
解决了我的问题。据我了解,如果在调用 ReadElementContentAsString()
时将 readNext
设置为 false,则 XmlReader 将不会调用 Read()
,因为我的 while 循环条件现在包含 (!readNext || xmlReader.Read())
。这可以防止 ReadElementContentAsString()
和 Read()
这两个方法紧接着被调用,因此,它不会跳过数据。
感谢 dbc!
您看到的问题是方法 XmlReader.ReadElementContentAsString
将 reader 移动到结束元素标记之后。如果您之后立即无条件地执行 xmlReader.Read()
,则紧跟在结束元素标记之后的节点将被 跳过 。在您的问题中显示的 XML 中,紧跟在结束元素标记之后的节点是 whitespace,因此您的问题无法重现该错误。但是如果我去掉缩进(希望你的 2+GB XML 文件没有缩进),这个错误就可以重现了。
此外,在您的问题中,我看不到您实际阅读 <ManagerId>7394</ManagerId>
标签的位置。相反,您只需从 currentManager.morningstarManagerId
(未定义的全局变量)中获取它。我认为这是你问题中的错字,在你的实际代码中你在某处读到了这个。
这是您的方法的一个版本,它修复了这些问题并且可以独立编译和测试:
public static DataTable Read(XmlReader xmlReader, Func<bool> continueProcess)
{
DataTable dtManagersEducation = new DataTable();
dtManagersEducation.TableName = "ManagersEducation";
dtManagersEducation.Columns.Add("ManagerId");
dtManagersEducation.Columns.Add("Institution");
dtManagersEducation.Columns.Add("DegreeType");
dtManagersEducation.Columns.Add("Emphasis");
dtManagersEducation.Columns.Add("Year");
bool inManagerDetail = false;
string managerId = null;
DataRow drManagersEducation = null;
bool readNext = true;
while ((!readNext || xmlReader.Read()) && continueProcess())
{
readNext = true;
if (xmlReader.NodeType == XmlNodeType.Element)
{
if (!xmlReader.IsEmptyElement)
{
if (xmlReader.Name == "ManagerDetail")
{
inManagerDetail = true;
}
else if (xmlReader.Name == "ManagerId")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (inManagerDetail)
managerId = value;
}
else if (xmlReader.Name == "School")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["Institution"] = value;
}
else if (xmlReader.Name == "Year")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["Year"] = value;
}
else if (xmlReader.Name == "Degree")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["DegreeType"] = value;
}
else if (xmlReader.Name == "Major")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["Emphasis"] = value;
}
else if (xmlReader.Name == "CollegeEducation")
{
if (managerId != null)
{
drManagersEducation = dtManagersEducation.NewRow();
drManagersEducation["ManagerId"] = managerId;
}
}
}
}
else if (xmlReader.NodeType == XmlNodeType.EndElement)
{
if (xmlReader.Name == "ManagerDetail")
{
inManagerDetail = false;
managerId = null;
}
else if (xmlReader.Name == "CollegeEducation")
{
if (drManagersEducation != null)
dtManagersEducation.Rows.Add(drManagersEducation);
drManagersEducation = null;
}
}
}
return dtManagersEducation;
}
我的应用程序中有一个 class 没有按预期转换我的 XML 数据。
XML数据
以下是 XML 的节选。文件大小介于 2 GB 和 3 GB 之间,数据代表共同基金。每个基金通常都有与之关联的经理,但也有可能列出 none 名。数据中的 Fund 可以有多个 ManagerDetail 节点,也可以没有 ManagerDetail 节点。每个经理可以有多个 CollegeEducation 节点或没有 CollegeEducation 节点。
<MutualFund>
<ManagerList>
<ManagerDetail>
<ManagerRole>M</ManagerRole>
<ManagerId>7394</ManagerId>
<ManagerTenure>3.67</ManagerTenure>
<StartDate>2011-09-30</StartDate>
<OwnershipLevel>6</OwnershipLevel>
<GivenName>Stephen</GivenName>
<MiddleName>M.</MiddleName>
<FamilyName>Kane</FamilyName>
<Gender>M</Gender>
<Certifications>
<CertificationName>CFA</CertificationName>
</Certifications>
<CollegeEducations>
<CollegeEducation>
<School>University of Chicago</School>
<Year>1990</Year>
<Degree>M.B.A.</Degree>
</CollegeEducation>
<CollegeEducation>
<School>University of California - Berkeley</School>
<Year>1985</Year>
<Degree>B.S.</Degree>
<Major>Business</Major>
</CollegeEducation>
</CollegeEducations>
</ManagerDetail>
</ManagerList>
</MutualFund>
C#Class
我创建了一个 class,它以另一种形式在 BackgroundWorker 实例中调用。这个class把上面的数据放到下面table:
public static DataTable dtManagersEducation = new DataTable();
dtManagersEducation.Columns.Add("ManagerId");
dtManagersEducation.Columns.Add("Institution");
dtManagersEducation.Columns.Add("DegreeType");
dtManagersEducation.Columns.Add("Emphasis");
dtManagersEducation.Columns.Add("Year");
放置XML数据的方法是这样设置的。基本上,我有一些创建和完成数据行的点,并且某些 XML 数据将在读取数据时放入可用行中。
public static void Read(MainForm mf, XmlReader xml)
{
mainForm = mf;
xmlReader = xml;
while (xmlReader.Read() && mainForm.continueProcess)
{
if (xmlReader.Name == "CollegeEducation")
{
if (nodeIsElement())
{
drManagersEducation = dtManagersEducation.NewRow();
drManagersEducation["ManagerId"] = currentManager.morningstarManagerId;
}
else if (nodeIsEndElement())
{
dtManagersEducation.Rows.Add(drManagersEducation);
drManagersEducation = null;
}
}
else if (xmlReader.Name == "School")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["Institution"] = value;
}
}
else if (xmlReader.Name == "Year")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["Year"] = value;
}
}
else if (xmlReader.Name == "Degree")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["DegreeType"] = value;
}
}
else if (xmlReader.Name == "Major")
{
if (nodeIsElement() && drManagersEducation != null)
{
string value = xmlReader.ReadElementContentAsString();
drManagersEducation["Emphasis"] = value;
}
}
}
}
private static bool nodeIsElement()
{
return xmlReader.NodeType == XmlNodeType.Element;
}
private static bool nodeIsEndElement()
{
return xmlReader.NodeType == XmlNodeType.EndElement;
}
结果最终在 Emphasis 或 Year 列中没有数据,正如您在上面看到的,有实例(大量)在这些字段中有数据。
ManagerId Institution DegreeType Emphasis Year
5807 Yale University M.S.
9336 Yale University
7227 Yale University M.S.
你们是否碰巧对正在发生的事情有所了解?
谢谢
编辑:回答
我上面列出的示例 XML 数据有缩进空格,但我通过 XmlReader 运行 得到的实际数据没有。如 dbc 所示,添加变量 bool readNext
解决了我的问题。据我了解,如果在调用 ReadElementContentAsString()
时将 readNext
设置为 false,则 XmlReader 将不会调用 Read()
,因为我的 while 循环条件现在包含 (!readNext || xmlReader.Read())
。这可以防止 ReadElementContentAsString()
和 Read()
这两个方法紧接着被调用,因此,它不会跳过数据。
感谢 dbc!
您看到的问题是方法 XmlReader.ReadElementContentAsString
将 reader 移动到结束元素标记之后。如果您之后立即无条件地执行 xmlReader.Read()
,则紧跟在结束元素标记之后的节点将被 跳过 。在您的问题中显示的 XML 中,紧跟在结束元素标记之后的节点是 whitespace,因此您的问题无法重现该错误。但是如果我去掉缩进(希望你的 2+GB XML 文件没有缩进),这个错误就可以重现了。
此外,在您的问题中,我看不到您实际阅读 <ManagerId>7394</ManagerId>
标签的位置。相反,您只需从 currentManager.morningstarManagerId
(未定义的全局变量)中获取它。我认为这是你问题中的错字,在你的实际代码中你在某处读到了这个。
这是您的方法的一个版本,它修复了这些问题并且可以独立编译和测试:
public static DataTable Read(XmlReader xmlReader, Func<bool> continueProcess)
{
DataTable dtManagersEducation = new DataTable();
dtManagersEducation.TableName = "ManagersEducation";
dtManagersEducation.Columns.Add("ManagerId");
dtManagersEducation.Columns.Add("Institution");
dtManagersEducation.Columns.Add("DegreeType");
dtManagersEducation.Columns.Add("Emphasis");
dtManagersEducation.Columns.Add("Year");
bool inManagerDetail = false;
string managerId = null;
DataRow drManagersEducation = null;
bool readNext = true;
while ((!readNext || xmlReader.Read()) && continueProcess())
{
readNext = true;
if (xmlReader.NodeType == XmlNodeType.Element)
{
if (!xmlReader.IsEmptyElement)
{
if (xmlReader.Name == "ManagerDetail")
{
inManagerDetail = true;
}
else if (xmlReader.Name == "ManagerId")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (inManagerDetail)
managerId = value;
}
else if (xmlReader.Name == "School")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["Institution"] = value;
}
else if (xmlReader.Name == "Year")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["Year"] = value;
}
else if (xmlReader.Name == "Degree")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["DegreeType"] = value;
}
else if (xmlReader.Name == "Major")
{
var value = xmlReader.ReadElementContentAsString();
readNext = false;
if (drManagersEducation != null)
drManagersEducation["Emphasis"] = value;
}
else if (xmlReader.Name == "CollegeEducation")
{
if (managerId != null)
{
drManagersEducation = dtManagersEducation.NewRow();
drManagersEducation["ManagerId"] = managerId;
}
}
}
}
else if (xmlReader.NodeType == XmlNodeType.EndElement)
{
if (xmlReader.Name == "ManagerDetail")
{
inManagerDetail = false;
managerId = null;
}
else if (xmlReader.Name == "CollegeEducation")
{
if (drManagersEducation != null)
dtManagersEducation.Rows.Add(drManagersEducation);
drManagersEducation = null;
}
}
}
return dtManagersEducation;
}