反序列化.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);
另一种更简单的方法:让解串器完成工作。
-
将
LineInfo
和LinePosition
属性添加到您想要获得位置信息的所有类:[XmlRoot("Root")] public class AddressDetails { [XmlAttribute] public int LineNumber { get; set; } [XmlAttribute] public int LinePosition { get; set; } ... }
这当然可以通过子类化来完成。
-
使用
LoadOptions.SetLineInfo
加载XDocument
。 -
将
LineInfo
和LinePosition
属性添加到所有元素:foreach (var element in xdoc.Descendants()) { var li = (IXmlLineInfo) element; element.SetAttributeValue("LineNumber", li.LineNumber); element.SetAttributeValue("LinePosition", li.LinePosition); }
-
反序列化将填充
LineInfo
和LinePosition
。
缺点:
- 行信息仅适用于反序列化为类的元素,不适用于简单元素,不适用于属性。
- 需要为所有类添加属性。
我尝试了几种方法。 他们两个人:
-
实现
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
。 (为了加快查找速度,可以使用包含所有XElement
的List
,以便NewId
是该列表中的索引。) -
将
XElement
为IXmlLineInfo
将提供行和位置信息。 -
对于尚未反序列化为类对象的元素(例如,示例中为
Number
)和属性,首先查找包含NewId
的XElement
(例如AdressDetails
),然后查询元素或属性的行信息:XElement element = ...; XNode descendant = element.Descendants(childElementOrAttributetName).FirstOrDefault(); return descendant as IXmlLineInfo;
缺点:
- 内存使用情况。
- 需要为所有类添加属性。