[案例] 我已经发现了一堆“xml文件”,其中包含有关其中大量文档的元数据。至少,这就是我的要求。我收到的是没有根元素的'xml文件',它们的结构是这样的(我遗漏了一堆元素):
<folder name = "abc"></folder>
<folder name = "abc/def">
<document name = "ghi1">
</document>
<document name = "ghi2">
</document>
</folder>
[问题] 当我尝试在XmlTextReader对象中读取文件时,它无法告诉我没有根元素。
[当前解决方法] 当然,我可以将文件作为流读取,追加&lt; xmlroot&GT;和&lt; / xmlroot&GT;并将流写入新文件并在XmlTextReader中读取该文件。这正是我现在正在做的事情,但我不想“篡改”原始数据。
[请求解决方案] 我知道我应该使用XmlTextReader,使用DocumentFragment选项。但是,这会产生编译时错误:
发生了'System.Xml.XmlException'类型的未处理异常 system.xml.dll的
其他信息:不支持XmlNodeType DocumentFragment 用于部分内容解析。第1行,第1位。
[错误代码]
using System.Diagnostics;
using System.Xml;
namespace XmlExample
{
class Program
{
static void Main(string[] args)
{
string file = @"C:\test.txt";
XmlTextReader tr = new XmlTextReader(file, XmlNodeType.DocumentFragment, null);
while(tr.Read())
Debug.WriteLine("NodeType: {0} NodeName: {1}", tr.NodeType, tr.Name);
}
}
}
答案 0 :(得分:3)
这有效:
using System.Diagnostics;
using System.Xml;
namespace XmlExample
{
class Program
{
static void Main(string[] args)
{
string file = @"C:\test.txt";
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
using (XmlReader reader = XmlReader.Create(file, settings))
{
while (reader.Read())
Debug.WriteLine("NodeType: {0} NodeName: {1}", reader.NodeType, reader.Name);
}
}
}
}
答案 1 :(得分:2)
即使XmlReader
可以使用Martijn演示的ConformanceLevel.Fragment
选项来读取数据,似乎XmlDataDocument
不喜欢拥有多个根元素的想法。
我以为我会尝试一种不同的方法,就像你当前使用的方法一样,但没有中间文件。大多数XML库(XmlDocument,XDocument,XmlDataDocument)可以将TextReader
作为输入,因此我实现了自己的一个。它的使用方式如下:
var dataDocument = new XmlDataDocument();
dataDocument.Load(new FakeRootStreamReader(File.OpenRead("test.xml")));
实际课程的代码:
public class FakeRootStreamReader : TextReader
{
private static readonly char[] _rootStart;
private static readonly char[] _rootEnd;
private readonly TextReader _innerReader;
private int _charsRead;
private bool _eof;
static FakeRootStreamReader()
{
_rootStart = "<root>".ToCharArray();
_rootEnd = "</root>".ToCharArray();
}
public FakeRootStreamReader(Stream stream)
{
_innerReader = new StreamReader(stream);
}
public FakeRootStreamReader(TextReader innerReader)
{
_innerReader = innerReader;
}
public override int Read(char[] buffer, int index, int count)
{
if (!_eof && _charsRead < _rootStart.Length)
{
// Prepend root element
return ReadFake(_rootStart, buffer, index, count);
}
if (!_eof)
{
// Normal reading operation
int charsRead = _innerReader.Read(buffer, index, count);
if (charsRead > 0) return charsRead;
// We've reached the end of the Stream
_eof = true;
_charsRead = 0;
}
// Append root element end tag at the end of the Stream
return ReadFake(_rootEnd, buffer, index, count);
}
private int ReadFake(char[] source, char[] buffer, int offset, int count)
{
int length = Math.Min(source.Length - _charsRead, count);
Array.Copy(source, _charsRead, buffer, offset, length);
_charsRead += length;
return length;
}
}
对Read(...)
的第一次调用只会返回<root>
元素。后续调用正常读取流,直到到达流的末尾,然后输出结束标记。
代码有点...... meh ...主要是因为我想处理一些永远不会发生的情况,有人试图一次读取少于6个字符的流。