使用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的类没有按预期抓取XML数据

您看到的问题是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;
    }