Bạn đã hiểu về việc tuần tự hóa .NET XML? [đóng cửa]


121

Tôi đã gặp phải một số vấn đề khi thực hiện tuần tự hóa XML C # mà tôi nghĩ rằng tôi sẽ chia sẻ:


using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{      
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
            return;

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");

            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement("item");

            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();

            writer.WriteStartElement("value");
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
}

Có bất kỳ yêu cầu nào khác về Serialization XML không?


Tìm kiếm thêm các mẹo khác lol, bạn có thể giúp tôi: stackoverflow.com/questions/2663836/…
Shimmy Weitzhandler,

1
Ngoài ra, bạn sẽ muốn xem cách triển khai từ điển serialzable của Charles Feduke, anh ấy đã khiến người viết xml chú ý giữa các thành viên được quy cho các thành viên thông thường sẽ được tuần tự hóa bởi bộ tuần tự mặc định: deployzone.com/2008/09/19/…
Shimmy Weitzhandler

Điều này có vẻ như nó không hoàn toàn nắm bắt được tất cả các gotchas. Tôi đang đặt IEqualityComparer trong hàm tạo, nhưng điều đó không được tuần tự hóa trong mã này. Bất kỳ ý tưởng nào về cách mở rộng Từ điển này để bao gồm chút thông tin này? thông tin đó có thể được xử lý thông qua đối tượng Type không?
ColinCren

Câu trả lời:


27

Một vấn đề lớn khác: khi xuất XML thông qua một trang web (ASP.NET), bạn không muốn bao gồm Dấu thứ tự Unicode . Tất nhiên, cách sử dụng hay không sử dụng BOM gần như giống nhau:

BAD (bao gồm BOM):

XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);

TỐT:

XmlTextWriter  wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))

Bạn có thể chuyển false một cách rõ ràng để cho biết bạn không muốn BOM. Lưu ý sự khác biệt rõ ràng, rõ ràng giữa Encoding.UTF8UTF8Encoding.

Ba byte BOM phụ ở đầu là (0xEFBBBF) hoặc (239 187 191).

Tham khảo: http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/


4
Nhận xét của bạn sẽ hữu ích hơn nếu bạn không chỉ cho chúng tôi biết điều gì, mà tại sao.
Neil

1
Đây không phải là thực sự liên quan đến XML serialization ... nó chỉ là một vấn đề XmlTextWriter
Thomas Levesque

7
-1: Không liên quan đến câu hỏi và bạn không nên sử dụng XmlTextWritertrong .NET 2.0 trở lên.
John Saunders

Liên kết tham khảo rất hữu ích. Cảm ơn.
Anil Vangari

21

Tôi chưa thể đưa ra nhận xét, vì vậy tôi sẽ bình luận về bài đăng của Dr8k và đưa ra nhận xét khác. Các biến private được hiển thị dưới dạng thuộc tính getter / setter công khai và được tuần tự hóa / deserialized thông qua các thuộc tính đó. Chúng tôi đã làm điều đó ở công việc cũ của tôi suốt thời gian qua.

Một điều cần lưu ý là nếu bạn có bất kỳ logic nào trong các thuộc tính đó, thì logic đó sẽ được chạy, vì vậy đôi khi, thứ tự tuần tự hóa thực sự quan trọng. Các thành viên được sắp xếp ngầm theo cách chúng được sắp xếp trong mã, nhưng không có đảm bảo nào, đặc biệt khi bạn đang kế thừa một đối tượng khác. Ra lệnh cho họ rõ ràng là một nỗi đau ở hậu phương.

Tôi đã bị bỏng bởi điều này trong quá khứ.


17
Tôi đã tìm thấy bài đăng này trong khi tìm kiếm các cách để đặt thứ tự các trường một cách rõ ràng. Điều này được thực hiện với các thuộc tính: [XmlElementAttribute (Order = 1)] public int Field {...} Downside: thuộc tính phải được chỉ định cho TẤT CẢ các trường trong lớp và tất cả các trường con của nó! IMO Bạn nên thêm cái này vào bài viết của mình.
Cristian Diaconescu

15

Khi tuần tự hóa thành một chuỗi XML từ một luồng bộ nhớ, hãy đảm bảo sử dụng MemoryStream # ToArray () thay vì MemoryStream # GetBuffer (), nếu không bạn sẽ gặp phải các ký tự rác không được giải mã đúng cách (do bộ đệm bổ sung được cấp phát).

http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(VS.80).aspx


3
trực tiếp từ tài liệu "Lưu ý rằng bộ đệm chứa các byte được cấp phát có thể không được sử dụng. Ví dụ: nếu chuỗi" kiểm tra "được ghi vào đối tượng MemoryStream, độ dài của bộ đệm được trả về từ GetBuffer là 256, không phải 4, với 252 byte không sử dụng. Để chỉ lấy dữ liệu trong bộ đệm, hãy sử dụng phương pháp ToArray; tuy nhiên, ToArray tạo một bản sao của dữ liệu trong bộ nhớ. " msdn.microsoft.com/en-us/library/…
realgt

chỉ bây giờ mới thấy điều này. Không còn nghe như vô nghĩa nữa.
John Saunders

Bạn chưa từng nghe điều này trước đây, điều này rất hữu ích khi gỡ lỗi.
Ricky

10

Nếu bộ tuần tự hóa gặp một thành viên / thuộc tính có giao diện như kiểu của nó, nó sẽ không tuần tự hóa. Ví dụ: phần sau sẽ không tuần tự hóa thành XML:

public class ValuePair
{
    public ICompareable Value1 { get; set; }
    public ICompareable Value2 { get; set; }
}

Mặc dù điều này sẽ tuần tự:

public class ValuePair
{
    public object Value1 { get; set; }
    public object Value2 { get; set; }
}

Nếu bạn nhận được một ngoại lệ với thông báo "Loại không được giải quyết cho thành viên ...", đây có thể là điều đang xảy ra.
Kyle Krull

9

IEnumerables<T>được tạo ra thông qua lợi nhuận không thể tuần tự hóa. Điều này là do trình biên dịch tạo ra một lớp riêng biệt để thực hiện trả về lợi nhuận và lớp đó không được đánh dấu là có thể tuần tự hóa.


Điều này áp dụng cho tuần tự hóa 'other', tức là thuộc tính [Serializable]. Tuy nhiên, điều này cũng không hoạt động đối với XmlSerializer.
Tim Robinson


8

Bạn không thể tuần tự hóa các thuộc tính chỉ đọc. Bạn phải có một getter và một setter, ngay cả khi bạn không bao giờ có ý định sử dụng deserialization để biến XML thành một đối tượng.

Vì lý do tương tự, bạn không thể tuần tự hóa các thuộc tính trả về giao diện: trình giải mã sẽ không biết lớp cụ thể nào cần khởi tạo.


1
Trên thực tế bạn có thể serialize một tài sản thu ngay cả khi nó không có setter, nhưng nó phải được khởi tạo trong constructor sao cho deserialization có thể thêm các mục vào nó
Thomas Levesque

7

Ồ, đây là một cái hay: vì mã tuần tự hóa XML được tạo và đặt trong một DLL riêng biệt, bạn không gặp bất kỳ lỗi có ý nghĩa nào khi có lỗi trong mã của bạn làm hỏng bộ tuần tự hóa. Giống như "không thể định vị s3d3fsdf.dll". Đẹp.


11
Bạn có thể tạo DLL đó trước thời hạn bằng cách sử dụng XML "Serializer Generator Tool (Sgen.exe)" và triển khai với ứng dụng của bạn.
huseyint

6

Không thể tuần tự hóa một đối tượng không có hàm tạo không tham số (chỉ bị cắn bởi đối tượng đó).

Và vì một số lý do, từ các thuộc tính sau, Giá trị được tuần tự hóa, nhưng không phải Tên đầy đủ:

    public string FullName { get; set; }
    public double Value { get; set; }

Tôi chưa bao giờ tìm hiểu lý do tại sao, tôi chỉ thay đổi Giá trị thành nội ...


4
Hàm tạo không tham số có thể là private / protected. Nó sẽ là đủ cho bộ tuần tự hóa XML. Vấn đề với FullName thực sự là kỳ lạ, không nên xảy ra ...
Max Galkin

@Yacoder: Có thể vì không phải double?mà chỉ double?
abatishchev

FullName có thể nullvà do đó sẽ không tạo ra bất kỳ XML nào khi được đăng nhiều kỳ
Jesper

5

Một điều nữa cần lưu ý: bạn không thể tuần tự hóa các thành viên lớp riêng tư / được bảo vệ nếu bạn đang sử dụng tuần tự hóa XML "mặc định".

Nhưng bạn có thể chỉ định logic tuần tự hóa XML tùy chỉnh triển khai IXmlSerializable trong lớp của mình và tuần tự hóa bất kỳ trường riêng tư nào bạn cần / muốn.

http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx


4

Nếu lắp ráp được tạo tuần tự hóa XML của bạn không ở trong cùng ngữ cảnh Tải như mã đang cố gắng sử dụng nó, bạn sẽ gặp phải các lỗi tuyệt vời như:

System.InvalidOperationException: There was an error generating the XML document.
---System.InvalidCastException: Unable to cast object
of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at
Microsoft.Xml.Serialization.GeneratedAssembly.
  XmlSerializationWriterSettings.Write3_Settings(Object o)

Nguyên nhân của điều này đối với tôi là một plugin được tải bằng cách sử dụng ngữ cảnh LoadFrom có nhiều bất lợi khi sử dụng ngữ cảnh tải. Khá thú vị khi theo dõi điều đó.




4

Nếu bạn cố gắng để serialize một mảng, List<T>hoặc IEnumerable<T>chứa các trường hợp của các lớp con của T, bạn cần phải sử dụng XmlArrayItemAttribute vào danh sách tất cả các phân nhóm được sử dụng. Nếu không, bạn sẽ nhận được một thông báo vô ích System.InvalidOperationExceptiontrong thời gian chạy khi bạn tuần tự hóa.

Đây là một phần của ví dụ đầy đủ từ tài liệu

public class Group
{  
   /* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base 
      type (Employee) and derived type (Manager) into serialized arrays. */

   [XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))]
   public Employee[] Employees;

3

Các biến / thuộc tính riêng không được tuần tự hóa trong cơ chế mặc định cho tuần tự hóa XML, nhưng là trong tuần tự hóa nhị phân.


2
Có, nếu bạn đang sử dụng tuần tự hóa XML "mặc định". Bạn có thể chỉ định logic tuần tự hóa XML tùy chỉnh triển khai IXmlSerializable trong lớp của mình và tuần tự hóa bất kỳ trường riêng tư nào bạn cần / muốn.
Max Galkin

1
Vâng, điều này là đúng. Tôi sẽ chỉnh sửa cái này. Nhưng việc triển khai giao diện đó thực sự là một khó khăn so với những gì tôi nhớ.
Charles Graham

3

Thuộc tính được đánh dấu bằng Obsoletethuộc tính không được tuần tự hóa. Tôi chưa thử nghiệm với Deprecatedthuộc tính nhưng tôi cho rằng nó sẽ hoạt động theo cách tương tự.


2

Tôi thực sự không thể giải thích điều này, nhưng tôi thấy điều này sẽ không nối tiếp:

[XmlElement("item")]
public myClass[] item
{
    get { return this.privateList.ToArray(); }
}

nhưng điều này sẽ:

[XmlElement("item")]
public List<myClass> item
{
    get { return this.privateList; }
}

Và cũng cần lưu ý rằng nếu bạn đang tuần tự đến một dòng ghi nhớ, bạn có thể muốn tìm đến 0 trước khi sử dụng nó.


Tôi nghĩ đó là vì nó không thể xây dựng lại nó. Trong ví dụ thứ hai, nó có thể gọi item.Add () để thêm các mục vào Danh sách. Nó không thể làm điều đó trong lần đầu tiên.
ilitirit

18
Sử dụng: [XmlArray ("item"), XmlArrayItem ("myClass", typeof (myClass))]
RvdK 16/02/09

1
Chúc mừng cho điều đó! học điều gì đó mỗi ngày
annakata 16/02/09

2

Hãy cẩn thận với các loại tuần tự hóa mà không tuần tự hóa rõ ràng, nó có thể dẫn đến sự chậm trễ trong khi .Net xây dựng chúng. Tôi đã phát hiện ra điều này gần đây trong khi tuần tự RSAParameters .


2

Nếu XSD của bạn sử dụng các nhóm thay thế, thì rất có thể bạn không thể (de) tự động tuần tự hóa nó. Bạn sẽ cần viết bộ tuần tự của riêng mình để xử lý tình huống này.

Ví dụ.

<xs:complexType name="MessageType" abstract="true">
    <xs:attributeGroup ref="commonMessageAttributes"/>
</xs:complexType>

<xs:element name="Message" type="MessageType"/>

<xs:element name="Envelope">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
            <xs:element ref="Message" minOccurs="0" maxOccurs="unbounded"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageA" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageB" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

Trong ví dụ này, một Phong bì có thể chứa Thư. Tuy nhiên, trình tuần tự mặc định của .NET không phân biệt giữa Message, ExampleMessageA và ExampleMessageB. Nó sẽ chỉ tuần tự hóa đến và đi từ lớp Thư cơ sở.


0

Các biến / thuộc tính riêng không được tuần tự hóa trong tuần tự hóa XML, nhưng ở trong tuần tự hóa nhị phân.

Tôi tin rằng điều này cũng có ích cho bạn nếu bạn đang tiết lộ các thành viên riêng tư thông qua các thuộc tính công khai - các thành viên riêng tư không được xếp thứ tự nên các thành viên công khai đều tham chiếu đến các giá trị rỗng.


Đây không phải là sự thật. Người thiết lập tài sản chung sẽ được gọi, và có lẽ sẽ đặt thành viên private.
John Saunders
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.