我有一个应用程序,它接受一个XML文档并按某些属性对其进行排序。我有与 XML 文档的每一行相关联的信息,我想将其包含在排序文档中。为了做到这一点,
当我加载文件时,我确保行信息是使用XDocument.Load(file, LoadOptions.SetLineInfo)加载的。
然后,我递归地遍历每个 XElement 并获取其行信息。当我运行该应用程序时,我注意到每个XElement都有两个注释,
- 类型之一 System.Xml.Linq.LineInfoAnnotation
- 和类型为System.Xml.Linq.LineInfoEndElementAnnotation。
它们包含我需要的信息,但在私人字段中。
我找不到有关这些类的任何信息,无法实例化它们,它们不会出现在System.Xml.Linq下的对象浏览器中。然而它们存在,我可以对它们运行"GetType()"并获取有关该类的信息。
如果它们存在,为什么它们不在 MSDN 引用中,为什么我不能实例化或扩展它们?为什么在对象浏览器中找不到它们?
附言 我的解决方法是使用反射来获取每个元素中包含的信息。但是我仍然无法传递类名来告诉方法它是什么类型,我必须将对象与XElement.Annotations(typeof(object))隔离开来,然后在其上运行GetType()。我在下面对此进行了说明。
public object GetInstanceField(Type type, object instance, string fieldName)
{
//reflective method that gets value of private field
}
XElement xEl = existingXElement; //existingXElement is passed in
var annotations = xEl.Annotations(typeof(object)); //contains two objects, start and end LineInfoAnnotation
var start = annotations.First();
var end = annotations.Last();
var startLineNumber = GetInstanceField(start.GetType(), start, lineNumber); //lineNumber is private field I'm trying to access.
var endLineNumber = GetInstanceField(end.GetType(), end, lineNumber);
这段代码有效,但同样,我不能只告诉方法"typeof(LineInfoAnnotation)",而是必须在现有对象上执行 GetType。我无法理解这一点。
这些类是私有的 - 如果你愿意的话,这是一个实现细节。
所有 XObjects(元素、属性)都实现 IXmlLineInfo 接口 - 但它们显式实现接口,因此您必须执行强制转换才能访问属性。
获得IXmlLineInfo后,您可以使用属性LineNumber和LinePosition。
var data =
@"<example>
<someElement
someAttribute=""val"">
</someElement></example>
";
var doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(data)), LoadOptions.SetLineInfo);
foreach(var element in doc.Descendants()) {
var elLineInfo = element as IXmlLineInfo;
Console.Out.WriteLine(
$"Element '{element.Name}' at {elLineInfo.LineNumber}:{elLineInfo.LinePosition}");
foreach(var attr in element.Attributes()) {
var attrLineInfo = attr as IXmlLineInfo;
Console.Out.WriteLine(
$"Attribute '{attr.Name}' at {attrLineInfo.LineNumber}:{attrLineInfo.LinePosition}");
}
}
输出:
Element 'example' at 1:2
Element 'someElement' at 2:2
Attribute 'someAttribute' at 3:3
要获取 EndElement 信息,您必须使用普通的旧 XML 读取器,因为 XObject api 不会公开有关元素结束位置的任何信息。
using(var reader = doc.CreateReader()) {
while(reader.Read()) {
var lineInfo = reader as IXmlLineInfo;
Console.Out.WriteLine($"{reader.NodeType} {reader.Name} at {lineInfo.LineNumber}:{lineInfo.LinePosition}");
if(reader.NodeType == XmlNodeType.Element && reader.HasAttributes) {
while(reader.MoveToNextAttribute()) {
Console.Out.WriteLine($"{reader.NodeType} {reader.Name} at {lineInfo.LineNumber}:{lineInfo.LinePosition}");
}
}
}
}
输出:
Element example at 1:2
Element someElement at 2:2
Attribute someAttribute at 3:3
EndElement someElement at 5:5
EndElement example at 5:19