2008-12-15 35 views
23

Đây là một ví dụ hư cấu của vấn đề tôi đang cố gắng giải quyết. Nếu tôi đang làm việc trong C# và có XML như thế này:Làm thế nào để deserialize chỉ một phần của một tài liệu XML trong C#

<?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 bên trong SalesPerson có thể có kích thước rất dài, megabyte. Tôi muốn deserialize thẻ, nhưng không deserialize phần tử XML SalesPerson thay vì giữ nó ở dạng thô "cho sau này".

Về bản chất tôi muốn có thể sử dụng điều này như là một biểu diễn đối tượng của 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; } 
} 

hợp tài sản trên đối tượng Ô tô nhân viên bán hàng sẽ chứa một dòng với xml liệu đó là trong phần tử xml < nhân viên bán hàng > sau khi chạy qua một XmlSerializer.

Việc này có thể thực hiện được không? Tôi có thể chọn chỉ deserialize "một phần của" một tài liệu xml?

Cảm ơn! -Mike

p.s. ví dụ xml bị đánh cắp từ How to Deserialize XML document

Trả lời

30

Nó có thể là một bit cũ thread, nhưng tôi sẽ đăng anyway. tôi đã có cùng một vấn đề (cần thiết để deserialize như 10kb dữ liệu từ một tập tin có nhiều hơn 1MB). Trong đối tượng chính (trong đó có một InnerObject mà cần phải được deserializer) tôi thực hiện một giao diện IXmlSerializable, sau đó thay đổi phương pháp ReadXml.

Chúng tôi có XmlTextReader như là đầu vào, dòng đầu tiên là đọc đến một thẻ XML:

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

Sau đó tạo XMLSerializer cho một loại đối tượng chúng tôi muốn deserialize và deserialize nó

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 

điều này đã tiết kiệm cho tôi rất nhiều thời gian để deserialize và cho phép tôi đọc chỉ là một phần của XML (chỉ một số chi tiết mô tả các tập tin, có thể giúp người dùng quyết định nếu tập tin là những gì ông muốn tải).

+0

Giải pháp tuyệt vời, tôi thấy rằng tôi cũng cần thiết để thiết lập gốc xml của đoạn để tránh một ngoại lệ với một ngoại lệ bên trong nói rằng ... xmlns = ''> không được mong đợi. Tôi đã thêm một câu trả lời cho giải pháp của tôi, vì giới hạn của các giới hạn độ dài bình luận. –

3

Bạn có thể kiểm soát cách tuần tự hóa của mình được thực hiện bằng cách triển khai giao diện ISerializable trong lớp của bạn. Lưu ý điều này cũng sẽ hàm ý một hàm tạo có chữ ký phương thức (thông tin SerializationInfo, ngữ cảnh StreamingContext) và chắc chắn bạn có thể làm những gì bạn đang yêu cầu. Tuy nhiên, hãy xem xét kỹ liệu bạn có thực sự cần phải thực hiện điều này bằng streaming hay không vì nếu bạn không phải sử dụng cơ chế truyền trực tuyến, việc đạt được điều tương tự với LINQ to XML sẽ dễ dàng hơn, và đơn giản hơn để duy trì trong dài hạn (IMO)

1

Thường thì việc deserialization XML là một đề xuất tất cả hoặc không có gì ra khỏi hộp, vì vậy có thể bạn sẽ cần phải tùy chỉnh. Nếu bạn không thực hiện deserialization đầy đủ, bạn chạy nguy cơ xml không đúng định dạng trong phần tử SalesPerson và do đó tài liệu không hợp lệ.

Nếu bạn sẵn sàng chấp nhận rủi ro đó, có thể bạn sẽ muốn thực hiện phân tích cú pháp văn bản cơ bản để tách các phần tử SalesPerson thành một tài liệu khác bằng cách sử dụng các cơ sở xử lý văn bản thuần túy, sau đó xử lý XML.

Đây là một ví dụ tốt về lý do tại sao XML không phải lúc nào cũng là câu trả lời đúng.

2

Tôi nghĩ người nhận xét trước là chính xác trong nhận xét của mình rằng XML có thể không phải là lựa chọn tốt nhất của cửa hàng sao lưu tại đây.

Nếu bạn đang gặp vấn đề về quy mô và không tận dụng lợi thế của một số tính năng khác mà bạn nhận được với XML, như biến đổi, bạn có thể nên sử dụng cơ sở dữ liệu cho dữ liệu của mình. Các hoạt động bạn đang thực sự dường như phù hợp hơn với mô hình đó.

Tôi biết điều này không thực sự trả lời câu hỏi của bạn, nhưng tôi nghĩ tôi sẽ làm nổi bật một giải pháp thay thế mà bạn có thể sử dụng. Một cơ sở dữ liệu tốt và một trình ánh xạ OR thích hợp như .netTiers, NHibernate, hoặc gần đây hơn LINQ to SQL/Entity Framework có lẽ sẽ giúp bạn sao lưu và chạy với những thay đổi tối thiểu đối với phần còn lại của codebase của bạn.

+1

cậu ấy có thể chỉ là một người tiêu dùng trên một esb.so anh không thể thay đổi phần datastore.reading của ông XMLs là một process.with hợp pháp một XmlReader ở mức độ thấp có thể chỉ một tập tin/stream và tìm kiếm/nhảy trực tiếp đến bất kỳ vị trí nào trong tài liệu. –

0

Bạn có thể kiểm soát những phần nào của lớp Ô tô được deserialized bằng cách thực hiện các giao diện IXmlSerializable trên lớp Ô tô, và sau đó trong ReadXml (XmlReader) phương pháp mà bạn sẽ đọc và deserialize các yếu tố xe nhưng khi bạn đạt đến yếu tố SalesPerson bạn sẽ đọc subtree của nó như là một chuỗi và sau đó xây dựng một Stream trên nội dung văn bản bằng cách sử dụng một StreamWriter.

Nếu bạn không bao giờ muốn XmlSerializer ghi phần tử SalesPerson, hãy sử dụng thuộc tính [XmlIgnore]. Tôi không chắc chắn những gì bạn muốn xảy ra khi bạn seriailize lớp Cars để đại diện XML của nó. Bạn đang cố gắng để chỉ ngăn chặn deserialization của SalesPerson trong khi vẫn có thể serialize đại diện XML của SalesPerson đại diện bởi các Stream?

Tôi có thể cung cấp ví dụ về điều này nếu bạn muốn triển khai cụ thể.

0

Nếu tất cả những gì bạn muốn làm là phân tích phần tử SalesPerson nhưng giữ nó như một chuỗi, bạn nên sử dụng Xsl Transform thay vì "Deserialization". Mặt khác, nếu bạn muốn phân tích phần tử SalesPerson và chỉ cư một đối tượng trong bộ nhớ từ tất cả các phần tử không phải SalesPerson khác, thì Xsl Transform cũng có thể là cách để đi. Nếu các tập tin là cách lớn, bạn có thể xem xét tách chúng và sử dụng Xsl để kết hợp các tập tin xml khác nhau để các SalesPerson I/O chỉ xảy ra khi bạn cần nó.

+0

Trường hợp sử dụng là dữ liệu Ô tô tôi muốn làm đối tượng để chương trình của tôi có thể tương tác với nó. XML SalesPerson đơn giản được gửi qua dây dẫn tới một hệ thống khác, vì vậy tôi thậm chí không cần phải kiểm tra nó. Về cơ bản, tôi cần phải có được tất cả các dữ liệu, nhưng chỉ quan tâm về những gì các yếu tố xe chứa. – Mike

+0

Nếu đó là trường hợp, thì tất cả những gì bạn phải làm là không cung cấp XmlElementAttributes để tuần tự hóa dữ liệu không phải ô tô. – devlord

+0

* deserialize, ý tôi là – devlord

1

Vui lòng thử xác định thuộc tính Người bán hàng làm loại XmlElement. Điều này làm việc cho đầu ra từ các dịch vụ web ASMX, sử dụng XML Serialization. Tôi nghĩ rằng nó sẽ làm việc trên đầu vào là tốt. Tôi sẽ mong đợi toàn bộ phần tử <SalesPerson> để gió lên trong XmlElement.

+0

Họ cũng có thể cần XmlAnyAttribute trên thành viên đó. –

+0

Bạn có thể nói lý do tại sao? –

+0

Tôi có thể bị nhầm lẫn, thực sự, vì có vẻ như XmlAny là một thuộc tính trả về * mảng * của XmlElements, không chỉ một. –

0

Tôi khuyên bạn nên đọc thủ công từ Xml, sử dụng bất kỳ phương pháp nhẹ nào, như XmlReader, XPathDocument hoặc LINQ-to-XML.

Khi bạn phải đọc chỉ có 3 thuộc tính, tôi giả sử bạn có thể viết mã mà tự đọc từ nút đó và có một kiểm soát toàn bộ thế nào nó được thực hiện thay vì dựa vào serialization/Deserialization

5

Các chấp nhận answer từ user271807 là một giải pháp tuyệt vời nhưng tôi tìm thấy, rằng tôi cũng cần thiết để thiết lập các gốc xml của đoạn để tránh một ngoại lệ với một ngoại lệ bên trong nói cái gì đó như thế này:

...xmlns=''> was not expected 

này ngoại lệ là trown khi tôi đã cố gắng để deserialize chỉ các yếu tố xác thực bên trong của tài liệu xml này:

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

vì vậy, tôi đã kết thúc việc tạo ra phương pháp mở rộng này như một giải pháp tái sử dụng - cảnh báo chứa am emory rò rỉ, xem dưới đây:

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

Cập nhật 20 tháng 3 năm 2017: Khi bình luận dưới đây chỉ ra, có một vấn đề rò rỉ bộ nhớ khi sử dụng một trong các nhà thầu của XmlSerializer, vì vậy tôi đã kết thúc bằng một giải pháp bộ nhớ đệm như hình dưới đây:

/// <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); 
    } 
} 
+0

Tôi đang làm việc trên một vấn đề tương tự và tôi đã tìm thấy câu hỏi của bạn và [bài đăng trên blog này] (https://blogs.msdn.microsoft.com/tess/2006/02/15/net-memory-leak-xmlserializing-your- way-to-a-memory-leak /) về rò rỉ bộ nhớ khi sử dụng hàm tạo XmlSerializer (Type, XmlRootAttribute). Bạn cần phải kiểm tra mã của bạn. Tôi nghĩ rằng phương pháp của bạn là tạo ra một hội đồng tạm thời mới mỗi khi nó được gọi. Có thể bạn sẽ phải thực hiện bộ nhớ đệm thủ công cho mỗi kết hợp Type + innerStartTag. – plushpuffin

+0

Có cảm ơn bạn đã nhắc tôi. Tôi đã cập nhật câu trả lời của mình với bản sửa lỗi. –

Các vấn đề liên quan