This articles provides an extended version of XmlDocument that keeps track of LineNumber and LinePosition of each node through the IXmlLineInfo interface.
XmlDocument class does contain the line number and line position information. With the exting .NET API, the only place you can access those values are XmlTextReader and XPathDocument.
If you simply would like to get the LineNumber and LinePosition while validating an XML file, I would suggest you to use the existing XmlReader class.
However, if you would like to randomly access the LineNumber and LinePosition of every Node in your XmlDocument, you will need to extend the XmlDocument.
Here is what we need to do:
- Create a new class by inheriting XmlDocument class.
- Create a new class by inheriting XmlElement (also XmlAttribute, XmlNode, etc.) and we will implement the IXmlLineInfo interface.
- Override the
void Load (XmlReader reader) method to store the reader.
- Override the
XmlElement CreateElement (...) method (and also the other Create methods for Attributes and Nodes) to return our new implementations.
Here is our XmlFileElement.cs, it will replace the XmlElement.
using System;
using System.Xml;
namespace Zdo.Xml
{
internal class XmlFileElement : XmlElement, IXmlLineInfo
{
#region Fields
int lineNumber;
int linePosition;
#endregion
#region Constructor
public XmlFileElement (string prefix, string localName,
string namespaceURI, XmlDocument doc)
: this(prefix, localName, namespaceURI, doc, 0, 0)
{
}
public XmlFileElement (string prefix, string localName,
string namespaceURI, XmlDocument doc,
int lineNumber, int linePosition)
: base(prefix, localName, namespaceURI, doc)
{
this.lineNumber = lineNumber;
this.linePosition = linePosition;
}
#endregion
#region IXmlLineInfo Members
public bool HasLineInfo ()
{
return true;
}
public int LineNumber
{
get { return lineNumber; }
}
public int LinePosition
{
get { return linePosition; }
}
#endregion
}
}
And here is our XmlFileDocument.cs, it will replace the XmlDocument.
using System;
using System.Xml;
namespace Zdo.Xml
{
internal class XmlFileDocument : XmlDocument
{
IXmlLineInfo lineInfo;
#region Constructor
public XmlFileDocument ()
: base()
{
}
#endregion
#region Methods
public override XmlElement CreateElement (string prefix,
string localName, string namespaceURI)
{
return (lineInfo != null)
? new XmlFileElement(prefix, localName, namespaceURI, this,
lineInfo.LineNumber, lineInfo.LinePosition)
: new XmlFileElement(prefix, localName, namespaceURI, this);
}
public override void Load (XmlReader reader)
{
if (reader is IXmlLineInfo)
lineInfo = (IXmlLineInfo) reader;
base.Load(reader);
lineInfo = null;
}
#endregion
}
}
And here is how we are going to use our new classes:
// Load an XML file with our new class
XmlDocument xmlDoc = new XmlFileDocument();
xmlDoc.Load("file.xml");
// Now get a node
XmlNode node = xmlDoc.SelectSingleNode("//book");
// It supports IXmlLineInfo
IXmlLineInfo lineInfo = (IXmlLineInfo) node;
Console.WriteLine(
String.Format("My node is at line number {0} and line position {1}",
lineInfo.LineNumber,
lineInfo.LinePosition));
Related Links:
If you found this post helpful, please "Kick" it so others can find it too: