在c#中将包含多个XML文件的单个大文件读入多个XML记录
本文关键字:文件 XML 记录 单个大 包含多 | 更新日期: 2023-09-27 18:10:47
我有一个文件,它有效地包含了相同格式的多个XML文件,因此该文件本身不是有效的XML;例如:
<?xml version='1.0' encoding='UTF-8'?>
<Proposal xmlns="a namespace">
<ASubnode>Text</ASubNode>
<LotsOfOtherNodes />
</Proposal>
<?xml version='1.0' encoding='UTF-8'?>
<Proposal xmlns="a namespace">
<ASubnode>Text</ASubNode>
<LotsOfOtherNodes />
</Proposal>
....
我想处理所有的Proposal节点,一次一个;例如:
foreach (var proposal in file)
do something
我不能使用XmlReader,因为它在到达中间XML声明节点时抛出异常。我可能会将整个文件读入字符串,然后使用Split方法,但这些文件的大小都是gb,因此作为一种选项,这并不是特别有吸引力。似乎我可以一次一行地读取文件,通过正则表达式搜索适当的节点,但是文件不是像上面那样每行一个节点的行格式,而是包含多个节点的非常长的行,并且在节点文本中随机换行。
有没有一种方法可以在不手工制作文本解析器的情况下实现这一点?
您有两个选择:
-
告诉XmlReader不要这么挑剔。设置XmlReaderSettings。一致性级别到一致性级别片段。这将使解析器忽略没有根节点的事实。
var settings = new XmlReaderSettings(); settings.ConformanceLevel = ConformanceLevel.Fragment; using (var reader = XmlReader.Create(textReader, settings)) { ... }
-
用'root'元素包装你的XML文件,这样你的文档将只有一个根节点
<?xml version='1.0' encoding='UTF-8'?> <root> <Proposal xmlns="a namespace"> <ASubnode>Text</ASubNode> <LotsOfOtherNodes /> </Proposal> <?xml version='1.0' encoding='UTF-8'?> <Proposal xmlns="a namespace"> <ASubnode>Text</ASubNode> <LotsOfOtherNodes /> </Proposal> .... </root>
您可以逐行读取文本而无需实际解析xml,因为xml文档的标题是相同的:
IEnumerable<XDocument> GetDocuments(Stream bulkStream)
{
var reader = new StreamReader(bulkStream);
var sb = new StringBuilder();
var firstLine = reader.ReadLine();
string line = firstLine;
while(line != null)
{
sb.Clear();
sb.Append(firstLine);
while((line = reader.ReadLine()) != null && line != firstLine)
{
sb.Append(line);
}
yield return XDocument.Parse(sb.ToString());
}
}
更新:即使声明可以在一行之间开始,下面也可以工作:
IEnumerable<XDocument> GetDocuments(Stream bulkStream)
{
const string decl = @"<?xml version='1.0' encoding='UTF-8'?>";
var sb = new StringBuilder();
bool start = true;
foreach(var line in GetLines(bulkStream).Where(l => !string.IsNullOrWhiteSpace(l)))
{
if (start)
{
if (line == decl)
start = false;
sb.AppendLine(line);
}
else
{
if (line == decl)
{
sb.ToString().Dump();
yield return XDocument.Parse(sb.ToString());
sb.Clear();
start = true;
sb.AppendLine(line);
}
else
sb.AppendLine(line);
}
}
sb.ToString().Dump();
yield return XDocument.Parse(sb.ToString());
}
IEnumerable<string> GetLines(Stream bulkStream)
{
const string decl = @"<?xml version='1.0' encoding='UTF-8'?>";
var reader = new StreamReader(bulkStream);
string line;
while((line = reader.ReadLine()) != null)
{
if (line.Contains(decl))
{
var declIndex = line.IndexOf(decl);
yield return line.Substring(0, declIndex);
yield return decl;
yield return line.Substring(declIndex + decl.Length);
}
else
{
yield return line;
}
}
}