反序列化.NET后的XML中的对象位置(行,列)

在使用XmlSerializer反序列化到.NET对象后,如何在xml标记的原始xml文件中获取位置?

这是一个示例XML

  4 ABC Bern   3 ABCD Prague   

XML到C#对象映射

 [XmlRoot("Root")] public class AddressDetails { [XmlElement("Number")] public int HouseNo; [XmlElement("Street")] public string StreetName; [XmlElement("CityName")] public string City; } 

期望的结果

  XmlSerializer serializer = new XmlSerializer(typeof(List)); var list = serializer.Deserialize(@"C:\Xml.txt") as List; // this is what I would like to do // getting information to origin of the property City of the 2nd object in the list var position = XmlSerializerHelper.GetPosition(o => list[1].City, @"C:\Xml.txt"); // should print "starts line=10, column=8" Console.WriteLine("starts line={0}, column={1}", position.Start.Line, position.Start.Column); // should print "ends line=10, column=35" Console.WriteLine("ends line={0}, column={1}", position.End.Line, position.Start.Column); // should print "type=XmlElement, name=CityName, value=Prague" Console.WriteLine("xml info type={0}, name={1}, value={2}", position.Type, position.Name, position.Value); 

另一种更简单的方法:让解串器完成工作。

  • LineInfoLinePosition属性添加到您想要获得位置信息的所有类:

     [XmlRoot("Root")] public class AddressDetails { [XmlAttribute] public int LineNumber { get; set; } [XmlAttribute] public int LinePosition { get; set; } ... } 

    这当然可以通过子类化来完成。

  • 使用LoadOptions.SetLineInfo加载XDocument

  • LineInfoLinePosition属性添加到所有元素:

     foreach (var element in xdoc.Descendants()) { var li = (IXmlLineInfo) element; element.SetAttributeValue("LineNumber", li.LineNumber); element.SetAttributeValue("LinePosition", li.LinePosition); } 
  • 反序列化将填充LineInfoLinePosition

缺点:

  • 行信息仅适用于反序列化为类的元素,不适用于简单元素,不适用于属性。
  • 需要为所有类添加属性。

我尝试了几种方法。 他们两个人:

  • 实现IXmlSerializable 。 原则上这可行,但是必须实现完整的反序列化机制。

  • 使用XmlReader反序列化,在某个static位置保存对该XmlReader的引用,并在反序列化的类的构造函数中,访问该XmlReader并检索行和位置信息。 缺点: static ; 比较复杂。

我没有找到比起始线和位置更多信息的方法,但老实说,这对我的用例来说已经足够了。

目前我想我将使用以下方法。

  • 使用行信息加载XML文件:

     XDocument xdoc = XDocument.Parse(xml, LoadOptions.SetLineInfo); 
  • 对于xdoc所有元素,添加一个允许识别元素的新属性(例如NewId ),例如,将第一个元素设为0,将第二个元素设为1,等等。

  • 确保所有反序列化的类(在您的情况下为AddressDetails )都将具有这样的NewId ,例如:

      [XmlAttribute] public int NewId { get; set; } 

    这可以通过从公共基类派生来完成,即使使用xsd.exe来创建类,这也是可能的,因为它会将所有类创建为partial类(例如,需要添加partial class foo : MyBaseClassElement { }所有课程的某个地方)。

  • 反序列化后,对于已作为类对象反序列化的每个元素,可以使用NewId查找已反序列化的XElement 。 (为了加快查找速度,可以使用包含所有XElementList ,以便NewId是该列表中的索引。)

  • XElementIXmlLineInfo将提供行和位置信息。

  • 对于尚未反序列化为类对象的元素(例如,示例中为Number )和属性,首先查找包含NewIdXElement (例如AdressDetails ),然后查询元素或属性的行信息:

     XElement element = ...; XNode descendant = element.Descendants(childElementOrAttributetName).FirstOrDefault(); return descendant as IXmlLineInfo; 

缺点:

  • 内存使用情况。
  • 需要为所有类添加属性。