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;
    }