Đây chỉ đơn giản là một hạn chế cố hữu của tuần tự hóa khai báo trong đó thông tin kiểu không được nhúng trong đầu ra.
Đang cố gắng chuyển đổi <Flibble Foo="10" />
lại thành
public class Flibble { public object Foo { get; set; } }
Làm thế nào để trình tuần tự biết liệu nó phải là một int, một chuỗi, một kép (hoặc một cái gì đó khác) ...
Để thực hiện công việc này, bạn có một số tùy chọn nhưng nếu bạn thực sự không biết cho đến thời gian chạy, cách dễ nhất để làm điều này có thể là sử dụng XmlAttributeOverrides .
Đáng buồn là điều này sẽ chỉ hoạt động với các lớp cơ sở, không phải giao diện. Tốt nhất bạn có thể làm ở đó là bỏ qua tài sản không đủ cho nhu cầu của bạn.
Nếu bạn thực sự phải ở lại với các giao diện, bạn có ba lựa chọn thực sự:
Giấu nó và xử lý nó trong một tài sản khác
Tấm lò hơi xấu xí, khó chịu và lặp đi lặp lại nhiều nhưng hầu hết người tiêu dùng của tầng lớp này sẽ không phải đối mặt với vấn đề:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { }
set { }
}
Điều này có thể trở thành một cơn ác mộng bảo trì ...
Triển khai IXmlSerializable
Tương tự như tùy chọn đầu tiên, bạn có toàn quyền kiểm soát mọi thứ nhưng
- Ưu điểm
- Bạn không có các thuộc tính 'giả' khó chịu quanh quẩn.
- bạn có thể tương tác trực tiếp với cấu trúc xml thêm tính linh hoạt / lập phiên bản
- Nhược điểm
- bạn có thể phải triển khai lại bánh xe cho tất cả các thuộc tính khác trên lớp
Các vấn đề về nỗ lực trùng lặp cũng tương tự như lần đầu tiên.
Sửa đổi thuộc tính của bạn để sử dụng kiểu gói
public sealed class XmlAnything<T> : IXmlSerializable
{
public XmlAnything() {}
public XmlAnything(T t) { this.Value = t;}
public T Value {get; set;}
public void WriteXml (XmlWriter writer)
{
if (Value == null)
{
writer.WriteAttributeString("type", "null");
return;
}
Type type = this.Value.GetType();
XmlSerializer serializer = new XmlSerializer(type);
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
serializer.Serialize(writer, this.Value);
}
public void ReadXml(XmlReader reader)
{
if(!reader.HasAttributes)
throw new FormatException("expected a type attribute!");
string type = reader.GetAttribute("type");
reader.Read();
if (type == "null")
return;
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
reader.ReadEndElement();
}
public XmlSchema GetSchema() { return(null); }
}
Sử dụng điều này sẽ liên quan đến một cái gì đó như (trong dự án P):
public namespace P
{
public interface IFoo {}
public class RealFoo : IFoo { public int X; }
public class OtherFoo : IFoo { public double X; }
public class Flibble
{
public XmlAnything<IFoo> Foo;
}
public static void Main(string[] args)
{
var x = new Flibble();
x.Foo = new XmlAnything<IFoo>(new RealFoo());
var s = new XmlSerializer(typeof(Flibble));
var sw = new StringWriter();
s.Serialize(sw, x);
Console.WriteLine(sw);
}
}
cung cấp cho bạn:
<?xml version="1.0" encoding="utf-16"?>
<MainClass
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<RealFoo>
<X>0</X>
</RealFoo>
</Foo>
</MainClass>
Điều này rõ ràng là cồng kềnh hơn cho người dùng của lớp mặc dù tránh được nhiều tấm lò hơi.
Một phương tiện hài lòng có thể hợp nhất ý tưởng XmlAnything vào thuộc tính 'hỗ trợ' của kỹ thuật đầu tiên. Bằng cách này, hầu hết công việc càu nhàu được thực hiện cho bạn nhưng những người tiêu dùng thuộc tầng lớp này không bị ảnh hưởng gì ngoài sự nhầm lẫn với việc xem xét nội tâm.