使用XmlReader的类没有按预期抓取XML数据
本文关键字:抓取 XML 数据 XmlReader 使用 | 更新日期: 2023-09-27 18:07:24
我的应用程序中有一个类没有按预期转换我的XML数据。
XML数据下面是XML的摘录。文件大小可以在2gb到3gb之间,数据是Mutual Funds的表示形式。每只基金通常都有与之相关的基金经理,但也有可能没有列出。数据中的基金可以有多个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#类我创建了一个在BackgroundWorker实例中以另一种形式调用的类。该类将上述数据放入下表中:
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数据的方法是这样设置的。基本上,我有创建和完成datarow的特定点,并且在读取数据时将某些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;
}
结果在强调或年份列中没有数据,正如您在上面看到的,有(很多)实例在这些字段中有数据。
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
方法将读者移动到结束元素标记之后。如果随后无条件执行xmlReader.Read()
,则结束元素标记之后的节点将被跳过。在您的问题中显示的XML中,结束元素标记之后的节点是空白,因此在您的问题中无法重现该错误。但是如果我去掉缩进(希望你的2+GB XML文件没有缩进),这个bug就会重现。
另外,在你的问题中,我看不出你实际上在哪里读取<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;
}