XMLReader.ReadToNextSibling 跳转到文件末尾

XMLReader.ReadToNextSibling jumps to end of file

我在 C# 中使用 XMLReader 从作为 Web 请求的一部分收到的 XML 文档中获取数据。 XML 是根据传递给服务的查询参数生成的,可以包含无数元素。此问题中的示例基于库存查询。

这是 XML 的精简版:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfQMXINVQ_INVENTORYType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<QMXINVQ_INVENTORYType>
    <ABCTYPE>C</ABCTYPE>
    <BINNUM>A-5-2</BINNUM>
    <FREQUNIT />
    <GLACCOUNT>
        <VALUE>????-???-200</VALUE>
        <GLCOMP
            glorder="2">200</GLCOMP>
    </GLACCOUNT>
    <INVENTORYID>140</INVENTORYID>
    <INVGENTYPE />
    <ISSUEUNIT>EACH</ISSUEUNIT>
    <VENDOR>ATI</VENDOR>
    <INVBALANCES>
        <BINNUM>A-5-2</BINNUM>
                    <CURBAL>6</CURBAL>
        <STAGEDCURBAL>0</STAGEDCURBAL>
        <STAGINGBIN>false</STAGINGBIN>
    </INVBALANCES>
    <ITEM>
        <DESCRIPTION>Connecting Link - Repair</DESCRIPTION>
        <EXTERNALREFID />
        <GROUPNAME />
        <ISSUEUNIT />
        <ITEMID>175</ITEMID>
        <ITEMTYPE>ITEM</ITEMTYPE>
        <LOTTYPE
            maxvalue="NOLOT">NOLOT</LOTTYPE>
    </ITEM>
</QMXINVQ_INVENTORYType>
<QMXINVQ_INVENTORYType>
    <ABCTYPE>C</ABCTYPE>
    <BINNUM>B-8-1</BINNUM>
    <FREQUNIT />
    <GLACCOUNT>
        <VALUE>????-???-300</VALUE>
        <GLCOMP
            glorder="2">300</GLCOMP>
    </GLACCOUNT>
    <INVENTORYID>142</INVENTORYID>
    <INVGENTYPE />
    <ISSUEUNIT>EACH</ISSUEUNIT>
    <VENDOR>ATI</VENDOR>
    <INVBALANCES>
        <BINNUM>B-8-1</BINNUM>
                    <CURBAL>5</CURBAL>
        <STAGEDCURBAL>0</STAGEDCURBAL>
        <STAGINGBIN>false</STAGINGBIN>
    </INVBALANCES>
    <ITEM>
        <DESCRIPTION>Fence Stretcher</DESCRIPTION>
        <EXTERNALREFID />
        <GROUPNAME />
        <ISSUEUNIT />
        <ITEMID>105</ITEMID>
        <ITEMTYPE>ITEM</ITEMTYPE>
        <LOTTYPE
            maxvalue="NOLOT">NOLOT</LOTTYPE>
    </ITEM>
  </QMXINVQ_INVENTORYType>

这里是有问题的代码块:

     XmlTextReader reader = new XmlTextReader(strRespFile);
     reader.MoveToContent();
     string topLevelElementName = reader.Name;
     // We need to advance "i" rows here.
     for (int k = 0; k < i; k++)
     {
        reader.ReadToNextSibling(topLevelElementName);
     }

     // Now, read the element's value for the column
     while (reader.Read())
     {
        if (reader.NodeType == XmlNodeType.Element)
        {
           if (reader.Name == c)
           {
              reader.Read();
              dr[c] = reader.Value;
              break;
           }
        }
     }
     reader.Close();

顶级代码(未显示)将 child 元素读取到数据表中。如果遇到 subchild,则执行包含的代码。第一个child元素的subchildren找到并成功添加到数据表中。

<INVBALANCES>
    <CURBAL>6</CURBAL>
...
<ITEM>
    <DESCRIPTION>Connecting Link - Repair</DESCRIPTION>

后续执行导致 reader 直接跳到文件末尾。关于为什么它会进入 EOF 而不是阅读下一个的任何想法 child?

问题是代码应该在进入 while 循环之前调用 MoveToFirstAttribute。

您正在将 reader 定位在 root element "ArrayOfQMXINVQ_INVENTORYType" 上,然后尝试读取其下一个 同级 。但是根元素不能有兄弟,因为只能有一个。你想要做的是:

  • 移动到根元素
  • 读到第一个child。
  • 跳到名为 topLevelElementName 的根的第 K 个 child 元素。
  • 搜索其 child 个元素。

例如:

        var xml = GetXml(); // Your sample XML as a string

        int topLevelElementIndex = 1;
        var topLevelElementName = "QMXINVQ_INVENTORYType";

        try
        {
            using (var sr = new StringReader(xml)) // You would use e.g. var sr = new StreamReader(strRespFile, Encoding.Encoding.UTF8)
            using (var reader = XmlReader.Create(sr))
            {
                // Move to ROOT Element
                reader.MoveToContent();
                Debug.Assert(reader.Name == "ArrayOfQMXINVQ_INVENTORYType"); // No assert.

                // Read to first CHILD Element of the root
                reader.Read(); // Read to the first child NODE of the root element
                reader.MoveToContent(); // If that node is whitespace, skip to content.

                // Read to the FIRST array element of the desired name.
                if (reader.Name != topLevelElementName)
                    if (!reader.ReadToNextSibling(topLevelElementName))
                        throw new InvalidOperationException("Not enough elements named " + topLevelElementName);
                // Now read to the Nth element of the desired name.
                for (int i = topLevelElementIndex; i > 0; i--)
                    if (!reader.ReadToNextSibling(topLevelElementName))
                        throw new InvalidOperationException("Not enough elements named " + topLevelElementName);

                // Process the Nth element as desired.
                Debug.Assert(reader.NodeType == XmlNodeType.Element && reader.Name == topLevelElementName);
                using (var subReader = reader.ReadSubtree())
                {
                    var element = XElement.Load(subReader);
                    Debug.WriteLine(element.ToString());
                    // If each individual array element has manageable size, the easiest way to parse it is to load it into
                    // an XElement with a nested reader, then use Linq to XML
                    Debug.Assert(element.Descendants("BINNUM").Select(e => (string)e).FirstOrDefault() == "B-8-1"); // No assert.
                }
            }
        }
        catch (Exception ex)
        {
            // Handle errors in the file however you like.
            Debug.WriteLine(ex);
            throw;
        }

例子fiddle.