2008-12-15 29 views
23

下面是我尝试解决的问题的虚构示例。如果我在C#我的工作,并且有这样的XML:如何反序列化C#中的部分XML文档

<?xml version="1.0" encoding="utf-8"?> 
<Cars> 
    <Car> 
    <StockNumber>1020</StockNumber> 
    <Make>Nissan</Make> 
    <Model>Sentra</Model> 
    </Car> 
    <Car> 
    <StockNumber>1010</StockNumber> 
    <Make>Toyota</Make> 
    <Model>Corolla</Model> 
    </Car> 
    <SalesPerson> 
    <Company>Acme Sales</Company> 
    <Position> 
     <Salary> 
      <Amount>1000</Amount> 
      <Unit>Dollars</Unit> 
    ... and on... and on.... 
    </SalesPerson> 
</Cars> 

内营业员XML可以很长,兆字节大小。我想反序列化标记,不反序列化SalesPerson XML元素,而是保留原始格式“供以后使用”。

本质上我想能够使用这个作为XML的对象表示。

[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; } 
} 

在汽车对象上营业员属性将包含与原始XML是正在通过一个XmlSerializer运行后<业务员> xml元素内的流。

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

谢谢! -Mike

p.s.示例xml被盗How to Deserialize XML document

回答

30

这可能是一个有点旧的线程,但我会发布反正。我遇到了同样的问题(需要反序列化一个文件中的数据超过1MB)。在主对象(它有一个InnerObject需要是反序列化器)我实现了一个IXmlSerializable接口,然后改变了ReadXml方法。

我们有XmlTextReader的输入,第一行是读取,直到一个XML标签:

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

那么对于一个类型的对象,我们要反序列化和反序列化

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 
创建的XMLSerializer

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

3

您可以通过在您的类中实现ISerializable接口来控制序列化的完成方式。注意这也意味着一个带方法签名(SerializationInfo info,StreamingContext上下文)的构造函数,并确保你可以做你正在问的东西。

但仔细看看是否真的需要使用流处理,因为如果您不必使用流式机制,那么使用Linq到XML实现相同的功能将会更容易,而且更简单长期维护(IMO)

1

通常,XML反序列化是一个开箱即用的命题,因此您可能需要自定义。如果您没有进行完全反序列化,那么可能会出现xml在SalesPerson元素中格式错误的风险,因此文档无效。

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

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

2

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

如果您遇到了规模问题并且没有充分利用XML获得的其他一些优势(如变换),那么最好使用数据库来处理数据。您正在进行的操作似乎更适合该模型。

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

+1

他可能只是上esb.so一个消费者,他不能改变他的个XML部分datastore.reading是合法process.with一个低级别的XMLReader可以索引的文件/流,并直接查找/跳转到文档中的任何位置。 – 2010-11-10 23:53:04

0

您可以控制哪些汽车类的部分通过实施对汽车类IXmlSerializable的界面反序列化,然后内的ReadXml(XmlReader中)方法,你会读和反序列化的汽车元素,但是当你到达SalesPerson元素可以将其子树作为字符串读取,然后使用StreamWriter在文本内容上构造一个Stream。

如果您不希望XmlSerializer的写出来的业务员元素,使用[XmlIgnore]属性。我不确定当你将Cars类用于其XML表示时,你想要发生什么。您是否试图仅阻止SalesPerson的反序列化,同时仍然能够序列化由Stream表示的SalesPerson的XML表示形式?

如果你想有一个具体的实现我大概可以提供这样的代码示例。

0

如果你想要做的是分析出营业员元素,但把它作为一个字符串,你应该使用XSL转换,而不是“反序列化”。另一方面,如果您想解析出SalesPerson元素并仅从所有其他非SalesPerson元素填充内存中的对象,那么Xsl Transform也可能是一条可行的路。如果文件的方式大,你可以考虑把它们分开,并使用XSL让营业员I/O只发生在你需要它来给不同的XML文件合并。

+0

用例是我想要的汽车数据作为对象,以便我的程序可以与它交互。 SalesPerson XML只是简单地通过网络发送到另一个系统,所以我甚至不需要检查它。基本上,我需要获取所有数据,但只关心Car元素包含的内容。 – Mike 2009-07-29 12:35:35

+0

如果是这种情况,那么您所要做的就是不提供XmlElementAttributes来序列化非汽车数据。 – devlord 2009-08-06 23:51:30

+0

*反序列化,我的意思是 – devlord 2009-08-06 23:52:04

1

请重定义销售人员财产XmlElement类型。这适用于使用XML序列化的ASMX Web服务的输出。我认为它也可以用于输入。我希望整个<SalesPerson>元素在XmlElement风。

+0

他们可能还需要该成员上的XmlAnyAttribute。 – 2009-07-31 16:44:19

0

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

当你不得不只读3个属性,我想你可以写代码,从该节点手动阅读和有它是如何执行的,而不是依靠序列化的完整控制/反序列化

5

接受的answer从user271807是一个很好的解决方案,但我发现,我还需要设置片段的XML根,以避免内部异常的异常说法是这样的:

...xmlns=''> was not expected 

这例外的是trown当我试图反序列化这个XML文档的只有内部验证元素:

<?xml version=""1.0"" encoding=""UTF-8""?> 
<Api> 
    <Authentication>      
     <sessionid>xxx</sessionid> 
     <errormessage>xxx</errormessage>     
    </Authentication> 
</ApI> 

我这样结束了创建这个扩展方法作为可重用解决方案- 警告含有上午埃默里泄漏,见下图:

public static T DeserializeXml<T>(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的构造函数之一时,内存泄漏问题,所以我结束了使用一个缓存解决方案如下图所示:

/// <summary> 
    ///  Deserialize XML string, optionally only an inner fragment of the XML, as specified by the innerStartTag parameter. 
    /// </summary> 
    public static T DeserializeXml<T>(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); 
      } 
     } 
    } 
/// <summary> 
///  A caching factory to avoid memory leaks in the XmlSerializer class. 
/// See http://dotnetcodebox.blogspot.dk/2013/01/xmlserializer-class-may-result-in.html 
/// </summary> 
public static class CachingXmlSerializerFactory { 
    private static readonly ConcurrentDictionary<string, XmlSerializer> Cache = new ConcurrentDictionary<string, XmlSerializer>(); 
    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<T>(XmlRootAttribute root) { 
     return Create(typeof (T), root); 
    } 
    public static XmlSerializer Create<T>() { 
     return Create(typeof (T)); 
    } 
    public static XmlSerializer Create<T>(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); 
    } 
}