Search code examples
c#xmllinqxelement

Cannot access or find reference to System.Xml.Linq.LineInfoAnnotation. Why is this?


I have an application which takes an XML document and sorts it by certain attributes. I have information associated with each line of the XML document which I want to include in the sorted document. In order to do this,

When I load the file, I make sure the line info is loaded using XDocument.Load(file, LoadOptions.SetLineInfo).

Then I recursively iterate over each XElement and get its line info. When I ran the app, I noticed that each XElement has two annotations,

  • one of type System.Xml.Linq.LineInfoAnnotation
  • and one of type System.Xml.Linq.LineInfoEndElementAnnotation.

They contain the info that I need but in private fields.

I can't find any information on these classes, I can't instantiate them, they do not appear in the Object browser under System.Xml.Linq. Yet they exist and I can run "GetType()" on them and get information about the class.

If they exist, why are they not in MSDN references and why can't I instantiate them or extend them? Why can't I find them in the object browser?

P.S. My workaround for this was to use reflection to get the information contained inside each element. But I still can't pass a class name to tell the method what type it is, I have to isolate the object from XElement.Annotations(typeof(object)), and then run GetType() on it. I've illustrated this below.

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);

This code works, but again, I can't just tell the method "typeof(LineInfoAnnotation)", instead I have to do GetType on the existing object. I cannot make sense of this.


Solution

  • Those classes are private - an implementation detail, if you will.

    All XObjects (elements, attributes) implement the IXmlLineInfo interface - but they implement the inteface explicitly, so you must perform a cast to access the properties.

    Once you have your IXmlLineInfo, you can use the properties LineNumber and 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}");
        }
    }
    

    Output:

    Element 'example' at 1:2
    Element 'someElement' at 2:2
    Attribute 'someAttribute' at 3:3
    

    To get the EndElement information, you have to use a plain old XML reader, since the XObject api doesn't expose any information about where the element ends.

    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}");
                }
            }
        }   
    }
    

    Output:

    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