我在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而不是读下一个孩子的任何想法?
答案 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。