Object position (line, column) in XML after deserialization .NET

匿名 (未验证) 提交于 2019-12-03 01:13:01

问题:

How can I get position in the original xml file of an xml tag after deserialization into a .NET object using XmlSerializer ?

Here is an example XML

                4        ABC        Bern                    3        ABCD        Prague         

XMLto C# object mapping

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

Desired result

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

回答1:

Another, more simple approach: Let the deserializer do the work.

  • Add LineInfo and LinePosition properties to all classes for which you would like to have position information:

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

    This of course can be done by subclassing.

  • Load an XDocument with LoadOptions.SetLineInfo.

  • Add LineInfo and LinePosition attributes to all elements:

    foreach (var element in xdoc.Descendants()) {      var li = (IXmlLineInfo) element;      element.SetAttributeValue("LineNumber", li.LineNumber);      element.SetAttributeValue("LinePosition", li.LinePosition);  }
  • Deserializing will populate LineInfo and LinePosition.

Cons:

  • Line information only for elements that are deserialized as class, not for simple elements, not for attributes.
  • Need to add attributes to all classes.


回答2:

I tried several approaches. Two of them:

  • Implement IXmlSerializable. In principle this would work, but then one would have to implement the complete deserialization mechanism.

  • Deserialize using an XmlReader, save a reference to that XmlReader at some static location, and in the constructor of classes that are deserialized, access that XmlReader and retrieve line and position information. Cons: static; relatively complicated.

I did not find a way to get more information than start line and position, but honestly, that’s enough for my use case.

Currently I think I will use the following approach.

  • Load the XML file with line information:

    XDocument xdoc = XDocument.Parse(xml, LoadOptions.SetLineInfo);
  • To all elements in xdoc, add a new attribute (e.g. NewId) that allows to identify the elements, e.g. give the first element a 0, the second a 1, etc.

  • Make sure all classes that are deserialized (in your case AddressDetails) will have such a NewId, e.g.:

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

    This can be done by deriving from a common base class, and even when xsd.exe is used to create the classes, this is possible because it will create all classes as partial (e.g. a partial class foo : MyBaseClassElement { } needs to be added somewhere for all classes).

  • After deserializing, for each element that has been deserialized as a class object, the XElement from which it has been deserialized can be looked up using NewId. (To speed up the look up, one can use a List that contains all XElement such that NewId is the index in that list.)

  • Casting the XElement to IXmlLineInfo will give line and position information.

  • For elements that have not been deserialized as a class object (e.g. Number in your example) and for attributes, first look up the XElement that contains a Ne

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!