XMLReader.ReadToNextSibling跳转到文件末尾

时间:2015-07-17 17:31:11

标签: c# xml xmlreader

我在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();

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

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

后续执行会导致读者直接跳到文件末尾。关于为什么要去EOF而不是读下一个孩子的任何想法?

2 个答案:

答案 0 :(得分:1)

您正在将阅读器放在root element "ArrayOfQMXINVQ_INVENTORYType"上,然后尝试阅读下一个兄弟。但根元素不能有兄弟,因为只有一个。你想要做的是:

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

例如:

        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

答案 1 :(得分:0)

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