如何在C#中仅反序列化部分XML文档

这是我试图解决的问题的一个虚构的例子。 如果我在C#中工作,并且像这样使用XML:

   1020 Nissan Sentra   1010 Toyota Corolla   Acme Sales   1000 Dollars ... and on... and on....   

SalesPerson中的XML可能非常长,大小为兆字节。 我想反序列化标记, 不反序列化SalesPerson XML元素,而是保持原始forms“以后”。

基本上我希望能够将其用作XML的Objects表示。

 [System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)] public class Cars { [XmlArrayItem(typeof(Car))] public Car[] Car { get; set; } public Stream SalesPerson { get; set; } } public class Car { [System.Xml.Serialization.XmlElementAttribute("StockNumber")] public string StockNumber{ get; set; } [System.Xml.Serialization.XmlElementAttribute("Make")] public string Make{ get; set; } [System.Xml.Serialization.XmlElementAttribute("Model")] public string Model{ get; set; } } 

其中Cars对象上的SalesPerson属性将包含一个流,该流具有在通过XmlSerializer运行后在 xml元素内的原始xml。

可以这样做吗? 我可以选择仅反序列化xml文档的“部分”吗?

谢谢! -麦克风

ps示例xml从如何反序列化XML文档中被盗

它可能有点旧线程,但无论如何我都会发帖。 我有同样的问题(需要从一个超过1MB的文件反序列化10kb的数据)。 在主对象(它有一个需要反序列化的InnerObject)中,我实现了一个IXmlSerializable接口,然后更改了ReadXml方法。

我们有xmlTextReader作为输入,第一行是读取XML标记:

 reader.ReadToDescendant("InnerObjectTag"); //tag which matches the InnerObject 

然后为我们要反序列化和反序列化的对象类型创建XMLSerializer

 XmlSerializer serializer = new XmlSerializer(typeof(InnerObject)); this.innerObject = serializer.Deserialize(reader.ReadSubtree()); //this gives serializer the part of XML that is for the innerObject data reader.close(); //now skip the rest 

这节省了我很多时间来反序列化,并允许我只读取XML的一部分(只是描述文件的一些细节,这可能有助于用户决定文件是否是他想要加载的)。

来自user271807的接受的答案是一个很好的解决方案,但我发现,我还需要设置片段的xml根,以避免exception,内部exception如下所示:

 ...xmlns=''> was not expected 

当我尝试仅反序列化此xml文档的内部Authentication元素时,会抛出此exception:

    xxx xxx   

所以我最终创建了这个扩展方法作为可重用的解决方案– 警告包含内存泄漏,见下文:

 public static T DeserializeXml(this string @this, string innerStartTag = null) { using (var stringReader = new StringReader(@this)) using (var xmlReader = XmlReader.Create(stringReader)) { if (innerStartTag != null) { xmlReader.ReadToDescendant(innerStartTag); var xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute(innerStartTag)); return (T)xmlSerializer.Deserialize(xmlReader.ReadSubtree()); } return (T)new XmlSerializer(typeof(T)).Deserialize(xmlReader); } } 

2017年3月20日更新:正如下面的评论所指出的,使用XmlSerializer的一个构造函数时存在内存泄漏问题,因此我最终使用了一个缓存解决方案,如下所示:

  ///  /// Deserialize XML string, optionally only an inner fragment of the XML, as specified by the innerStartTag parameter. ///  public static T DeserializeXml(this string @this, string innerStartTag = null) { using (var stringReader = new StringReader(@this)) { using (var xmlReader = XmlReader.Create(stringReader)) { if (innerStartTag != null) { xmlReader.ReadToDescendant(innerStartTag); var xmlSerializer = CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute(innerStartTag)); return (T) xmlSerializer.Deserialize(xmlReader.ReadSubtree()); } return (T) CachingXmlSerializerFactory.Create(typeof (T), new XmlRootAttribute("AutochartistAPI")).Deserialize(xmlReader); } } } ///  /// A caching factory to avoid memory leaks in the XmlSerializer class. /// See http://dotnetcodebox.blogspot.dk/2013/01/xmlserializer-class-may-result-in.html ///  public static class CachingXmlSerializerFactory { private static readonly ConcurrentDictionary Cache = new ConcurrentDictionary(); public static XmlSerializer Create(Type type, XmlRootAttribute root) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (root == null) { throw new ArgumentNullException(nameof(root)); } var key = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName); return Cache.GetOrAdd(key, _ => new XmlSerializer(type, root)); } public static XmlSerializer Create(XmlRootAttribute root) { return Create(typeof (T), root); } public static XmlSerializer Create() { return Create(typeof (T)); } public static XmlSerializer Create(string defaultNamespace) { return Create(typeof (T), defaultNamespace); } public static XmlSerializer Create(Type type) { return new XmlSerializer(type); } public static XmlSerializer Create(Type type, string defaultNamespace) { return new XmlSerializer(type, defaultNamespace); } } 

您可以通过在类中实现ISerializable接口来控制序列化的完成方式。 请注意,这也意味着带有方法签名(SerializationInfo info,StreamingContext context)的构造函数,并确保您可以执行您所要求的操作。

但是仔细看看你是否真的需要使用流式传输,因为如果你不必使用流式传输机制,那么使用Linq to XML实现同样的function会更容易,并且更难以长期维护期限(IMO)

我认为之前的评论者在他的评论中是正确的,XML可能不是这里支持商店的最佳选择。

如果您遇到规模问题并且没有利用XML所带来的其他一些细节,比如转换,那么最好使用数据库来处理数据。 您正在进行的操作似乎更适合该模型。

我知道这并没有真正回答你的问题,但我想我会强调你可能会使用的替代解决方案。 一个好的数据库和一个适当的OR映射器,如.netTiers,NHibernate,或者最近的LINQ to SQL / Entity Framework,可能会让你备份并运行,只需对代码库的其余部分进行最小的更改。

通常,XML反序列化是开箱即用的全有或全无的命题,因此您可能需要自定义。 如果不执行完全反序列化,则会冒着在SalesPerson元素中xml格式错误的风险,因此文档无效。

如果您愿意接受这种风险,您可能希望进行一些基本的文本解析,以使用纯文本处理工具将SalesPerson元素分解为不同的文档,然后处理XML。

这是为什么XML并不总是正确答案的一个很好的例子。

请尝试将SalesPerson属性定义为XmlElement类型。 这适用于使用XML序列化的ASMX Web服务的输出。 我认为它也适用于输入。 我希望整个元素在XmlElement

您可以通过在Cars类上实现IXmlSerializable接口来控制Cars类的哪些部分反序列化,然后在ReadXml(XmlReader)方法中读取和反序列化Car元素,但是当您到达SalesPerson元素时,您将读取其子树作为字符串,然后使用StreamWriter在文本内容上构建Stream。

如果您不希望XmlSerializer写出SalesPerson元素,请使用[XmlIgnore]属性。 当你将Cars类序列化为XML表示时,我不确定你想要发生什么。 您是否尝试仅阻止SalesPerson的反序列化,同时仍能序列化Stream表示的SalesPerson的XML表示forms?

如果你想要一个具体的实现,我可能会提供一个代码示例。

如果您只想解析SalesPerson元素但将其保留为字符串,则应使用Xsl Transform而不是“Deserialization”。 另一方面,如果要解析SalesPerson元素并仅从所有其他非SalesPerson元素填充内存中的对象,那么Xsl Transform也可能是最佳选择。 如果文件很大,您可以考虑将它们分开并使用Xsl组合不同的xml文件,以便SalesPerson I / O仅在您需要时才会出现。

我建议你使用任何轻量级方法从Xml手动读取,如XmlReader,XPathDocument或LINQ-to-XML。

当您只需要读取3个属性时,我想您可以编写从该节点手动读取的代码,并完全控制它的执行方式,而不是依赖于序列化/反序列化