2010-04-22 28 views
23

Tôi có các lớp sauLàm thế nào để thêm thuộc tính XmlInclude động

[XmlRoot] 
public class AList 
{ 
    public List<B> ListOfBs {get; set;} 
} 

public class B 
{ 
    public string BaseProperty {get; set;} 
} 

public class C : B 
{ 
    public string SomeProperty {get; set;} 
} 

public class Main 
{ 
    public static void Main(string[] args) 
    { 
     var aList = new AList(); 
     aList.ListOfBs = new List<B>(); 
     var c = new C { BaseProperty = "Base", SomeProperty = "Some" }; 
     aList.ListOfBs.Add(c); 

     var type = typeof (AList); 
     var serializer = new XmlSerializer(type); 
     TextWriter w = new StringWriter(); 
     serializer.Serialize(w, aList); 
    }  
} 

Bây giờ khi tôi cố gắng chạy đoạn code tôi có một InvalidOperationException ở dòng cuối cùng nói rằng

Loại XmlTest.C không được trông đợi . Sử dụng thuộc tính XmlInclude hoặc SoapInclude để chỉ định các kiểu không được biết đến tĩnh.

Tôi biết rằng việc thêm thuộc tính [XmlInclude (typeof (C))] với [XmlRoot] sẽ giải quyết được sự cố. Nhưng tôi muốn đạt được nó một cách năng động. Bởi vì trong lớp C dự án của tôi không được biết trước khi tải. Lớp C đang được tải dưới dạng plugin, vì vậy tôi không thể thêm thuộc tính XmlInclude vào đó.

Tôi cũng đã cố gắng với

TypeDescriptor.AddAttributes(typeof(AList), new[] { new XmlIncludeAttribute(c.GetType()) }); 

trước

var type = typeof (AList); 

nhưng không sử dụng. Nó vẫn cho cùng một ngoại lệ.

Có ai có ý tưởng nào về cách đạt được điều đó không?

Trả lời

28

Hai tùy chọn; đơn giản nhất (nhưng cho xml lẻ) là:

XmlSerializer ser = new XmlSerializer(typeof(AList), 
    new Type[] {typeof(B), typeof(C)}); 

Với ví dụ đầu ra:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <ListOfBs> 
    <B /> 
    <B xsi:type="C" /> 
    </ListOfBs> 
</AList> 

thanh lịch hơn là:

XmlAttributeOverrides aor = new XmlAttributeOverrides(); 
XmlAttributes listAttribs = new XmlAttributes(); 
listAttribs.XmlElements.Add(new XmlElementAttribute("b", typeof(B))); 
listAttribs.XmlElements.Add(new XmlElementAttribute("c", typeof(C))); 
aor.Add(typeof(AList), "ListOfBs", listAttribs); 

XmlSerializer ser = new XmlSerializer(typeof(AList), aor); 

Với ví dụ đầu ra:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <b /> 
    <c /> 
</AList> 

Trong cả hai trường hợp, bạn phải bộ nhớ cache và tái sử dụng cá thể ser; nếu không bạn sẽ xuất huyết bộ nhớ từ biên dịch động.

+1

Tôi đã thử cách này nhưng nó cho tôi ngoại lệ sau đây "Thuộc tính XmlRoot và XmlType có thể không được chỉ định cho loại XmlTest.AList." Bất kỳ ý tưởng nào tại sao lại đến và cách giải quyết? –

+0

@Anindya - đó là lẻ; ví dụ "as is" hoạt động tốt cho tôi. Bạn đang sử dụng phiên bản khung công tác nào? –

+0

@Anindya - Tôi đã thử trên 4.0 và 2.0 (bao gồm 3.0 và 3.5) và không thể tái tạo. Bạn có thể mô tả đầy đủ hơn về điều này không? –

2

Hãy xem tài liệu về XmlSerializer. Có một hàm tạo mong đợi các kiểu đã biết làm tham số thứ hai. Điều đó sẽ làm việc tốt cho bạn sử dụng trường hợp.

0

Tôi không nghĩ rằng các thuộc tính có thể được áp dụng trong thời gian chạy, vì chúng được sử dụng để tạo Siêu dữ liệu tại mã CIL.

6

Xây dựng dựa trên câu trả lời đầu tiên của Marc (tôi chỉ cần đọc, vì vậy tôi không cần phải ngăn đầu ra lạ), tôi sử dụng mảng kiểu động/chung hơn để tính các loại không xác định, được lấy cảm hứng từ số codeproject này.

public static XmlSerializer GetSerializer() 
    { 
     var lListOfBs = (from lAssembly in AppDomain.CurrentDomain.GetAssemblies() 
          from lType in lAssembly.GetTypes() 
          where typeof(B).IsAssignableFrom(lType) 
          select lType).ToArray(); 
     return new XmlSerializer(typeof(AList), lListOfBs); 
    } 

(Một có thể có thể làm cho nó hiệu quả hơn, ví dụ như sử dụng một tĩnh hoặc read-only kiểu mảng trong thay vì một biến địa phương. Điều đó sẽ tránh liên tục sử dụng Reflection. Nhưng tôi không biết đủ về việc khi nào Việc sử dụng của tôi không phải là quá nhiều, để dành thời gian để điều tra tất cả điều này, vì vậy tôi chỉ sử dụng cùng một Reflection nhiều lần.)

+0

làm việc như một sự quyến rũ, cảm ơn bạn (và marc) rất nhiều – Tiax

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