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

前端 未结 2 592
我寻月下人不归
我寻月下人不归 2020-12-17 02:54

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

          


        
相关标签:
2条回答
  • 2020-12-17 03:15

    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.
    0 讨论(0)
  • 2020-12-17 03:16

    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<XElement> 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 NewId (e.g. AdressDetails), then query the element’s or attribute’s line information:

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

    Cons:

    • Memory usage.
    • Need to add attribute to all classes.
    0 讨论(0)
提交回复
热议问题