Vui lòng xem mã bên dưới viết XML ra để gửi một lớp đơn giản chứa danh sách 3 đối tượng. 3 đối tượng trong danh sách đi xuống từ nhau, Base, Derived1, Derived2. Tôi sử dụng XMLArrayItemAttributes để ghi đè các tên trong quá trình tuần tự hóa. Điều này làm việc tốt trong .NET 3.0, nhưng bây giờ kết quả đầu ra khác nhau trong .NET 4.0. Vui lòng xem các Đầu ra dưới đây, lưu ý cụ thể mục con thứ hai DerivedItem2.Nối tiếp XML - kết quả khác nhau trong .NET 4.0
Có ai có kinh nghiệm với điều này và cách tôi có thể sửa chữa nó để làm việc trong .NET 4.0 như nó đã làm trong v3.5?
Dường như tôi không thể kiểm soát thứ tự các mục mảng bị ghi đè. Nó không xuất hiện là thứ tự mà chúng được thêm vào XMLArrayItems.
Chỉnh sửa: Tôi vừa thử ví dụ tương tự bằng cách sử dụng MONO dựa trên các phiên bản 4.0 và 4.5 và nó hoạt động tốt với các phiên bản đó. Có thể đây chỉ là một lỗi với các phiên bản khung công tác của Microsoft không?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.IO;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
TestGroup g = new TestGroup();
XmlSerializer s = new XmlSerializer(typeof(TestGroup), g.GetOverrides());
TextWriter w = new StreamWriter("c:\\#\\test.xml");
s.Serialize(w, g);
w.Close();
}
}
public class TestGroup
{
public List<BaseItem> Items { get; set; }
public TestGroup()
{
Items = new List<BaseItem>();
BaseItem b = new BaseItem();
b.BaseName = "Base Name";
Items.Add(b);
DerivedItem d1 = new DerivedItem();
d1.BaseName = "D1";
d1.DerivedName = "D1";
Items.Add(d1);
DerivedItem2 d2 = new DerivedItem2();
d2.BaseName = "D2";
//d2.DerivedName = "D2";
d2.Derived2Name = "D2";
Items.Add(d2);
}
public XmlAttributeOverrides GetOverrides()
{
XmlAttributes atts = new XmlAttributes();
for (int i = 0; i < Items.Count; i++)
{
BaseItem b = Items[i];
Type ItemType = b.GetType();
XmlArrayItemAttribute ItemAtt = new XmlArrayItemAttribute();
ItemAtt.ElementName = ItemType.Name;
ItemAtt.Type = ItemType;
atts.XmlArrayItems.Add(ItemAtt);
}
XmlAttributeOverrides attOvers = new XmlAttributeOverrides();
attOvers.Add(typeof(TestGroup), "Items", atts);
return attOvers;
}
}
public class BaseItem : IXmlSerializable
{
public string BaseName;
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
// not required for example
}
public virtual void WriteXml(XmlWriter writer)
{
writer.WriteElementString("BaseName", this.BaseName);
}
}
public class DerivedItem: BaseItem
{
public string DerivedName;
public override void WriteXml(XmlWriter writer)
{
base.WriteXml(writer);
writer.WriteElementString("DerivedName", this.DerivedName);
}
}
public class DerivedItem2: DerivedItem
{
public string Derived2Name;
public override void WriteXml(XmlWriter writer)
{
base.WriteXml(writer);
writer.WriteElementString("Derived2Name", this.Derived2Name);
}
}
Output gốc (.NET 3.0):
<?xml version="1.0" encoding="utf-8"?>
<TestGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<BaseItem>
<BaseName>Base Name</BaseName>
</BaseItem>
<DerivedItem>
<BaseName>D1</BaseName>
<DerivedName>D1</DerivedName>
</DerivedItem>
<DerivedItem2>
<BaseName>D2</BaseName>
<DerivedName />
<Derived2Name>D2</Derived2Name>
</DerivedItem2>
</Items>
</TestGroup>
Output Changed (.NET 4.0):
<?xml version="1.0" encoding="utf-8"?>
<TestGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<BaseItem>
<BaseName>Base Name</BaseName>
</BaseItem>
<DerivedItem>
<BaseName>D1</BaseName>
<DerivedName>D1</DerivedName>
</DerivedItem>
<DerivedItem>
<BaseName>D2</BaseName>
<DerivedName />
<Derived2Name>D2</Derived2Name>
</DerivedItem>
</Items>
</TestGroup>
Cập nhật: Output từ NET 4,5
<?xml version="1.0" encoding="utf-8"?>
<TestGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Items>
<BaseItem>
<BaseName>Base Name</BaseName>
</BaseItem>
<BaseItem>
<BaseName>D1</BaseName>
<DerivedName>D1</DerivedName>
</BaseItem>
<DerivedItem2>
<BaseName>D2</BaseName>
<DerivedName />
<Derived2Name>D2</Derived2Name>
</DerivedItem2>
</Items>
</TestGroup>
Cập nhật: Bật o n công tắc gỡ lỗi trong app.config như dưới đây, được tham chiếu từ http://msdn.microsoft.com/en-us/library/aa302290.aspx, tôi thấy rằng thứ tự mà Serialization áp dụng các ghi đè khác với thứ tự mà tôi điền vào mảng ghi đè. Bất cứ ai cũng có ý tưởng làm thế nào thứ tự này được xác định hoặc ghi đè?
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<switches>
<add name="XmlSerialization.Compilation" value="4" />
</switches>
</system.diagnostics>
</configuration>
này mang lại cho tôi aC# ra tập tin trong đó cho thấy trình tự ghi đè như không chính xác:
void Write2_TestGroup(string n, string ns, global::WindowsFormsApplication1.TestGroup o, bool isNullable, bool needType) {
if ((object)o == null) {
if (isNullable) WriteNullTagLiteral(n, ns);
return;
}
if (!needType) {
System.Type t = o.GetType();
if (t == typeof(global::WindowsFormsApplication1.TestGroup)) {
}
else {
throw CreateUnknownTypeException(o);
}
}
WriteStartElement(n, ns, o, false, null);
if (needType) WriteXsiType(@"TestGroup", @"");
{
global::System.Collections.Generic.List<global::WindowsFormsApplication1.BaseItem> a = (global::System.Collections.Generic.List<global::WindowsFormsApplication1.BaseItem>)((global::System.Collections.Generic.List<global::WindowsFormsApplication1.BaseItem>)[email protected]);
if (a != null){
WriteStartElement(@"Items", @"", null, false);
for (int ia = 0; ia < ((System.Collections.ICollection)a).Count; ia++) {
global::WindowsFormsApplication1.BaseItem ai = (global::WindowsFormsApplication1.BaseItem)a[ia];
if ((object)(ai) != null){
if (ai is global::WindowsFormsApplication1.DerivedItem) {
WriteSerializable((System.Xml.Serialization.IXmlSerializable)((global::WindowsFormsApplication1.DerivedItem)ai), @"DerivedItem", @"", true, true);
}
else if (ai is global::WindowsFormsApplication1.BaseItem) {
WriteSerializable((System.Xml.Serialization.IXmlSerializable)((global::WindowsFormsApplication1.BaseItem)ai), @"BaseItem", @"", true, true);
}
else if (ai is global::WindowsFormsApplication1.DerivedItem2) {
WriteSerializable((System.Xml.Serialization.IXmlSerializable)((global::WindowsFormsApplication1.DerivedItem2)ai), @"DerivedItem2", @"", true, true);
}
else if ((object)(ai) != null){
throw CreateUnknownTypeException(ai);
}
}
}
WriteEndElement();
}
}
WriteEndElement(o);
}
Vâng, điều này có vẻ lạ. Bạn đã thử với .net 4.5 chưa? Thứ tự loại 'ai' được kiểm tra chỉ nên là từ loại chuyên biệt nhất đến loại tổng quát nhất, theo suy nghĩ của tôi. Bất cứ điều gì khác không có ý nghĩa vì nó sẽ không bao giờ xuất ra tên kiểu đúng trong XML kèm theo và do đó deserialising sẽ không hoạt động. Bạn đã thử deserialising XML được tạo ra chưa? Bạn có nhận được hành vi tương tự nếu bạn không ghi đè WriteXml() không? – Rory
Có thể có một số khía cạnh khó tính đối với serialiser, ví dụ: bạn đã thử bảo đảm các lớp dẫn xuất của bạn đều có phương thức ReadXml() chưa? – Rory
Cảm ơn các bình luận Rory. Tôi đã thử một vài kết hợp bao gồm/không bao gồm ReadXml/WriteXml và nó vẫn có vẻ như cung cấp cho cùng một tên phần tử cơ sở. Cũng đã thử nó với .NET 4.5 ở cả hai chế độ cũ và không nhưng vẫn không có sự khác biệt. –