2009-03-09 28 views
15

Tôi đang gửi xml đến một chương trình khác, dự kiến ​​cờ boolean là "có" hoặc "không", thay vì "true" hoặc "false".Làm thế nào tôi có thể nhận XmlSerializer để mã hóa các bool là có/không?

Tôi đã một lớp được định nghĩa như sau:

[XmlRoot()] 
public class Foo { 
    public bool Bar { get; set; } 
} 

Khi tôi serialize nó, đầu ra của tôi trông như thế này:

<Foo><Bar>true</Bar></Foo> 

Nhưng tôi muốn nó là thế này:

<Foo><Bar>yes</Bar></Foo> 

Tôi có thể làm điều này tại thời điểm serialization không? Tôi không muốn phải nghỉ mát để này:

[XmlRoot()] 
public class Foo { 
    [XmlIgnore()] 
    public bool Bar { get; set; } 

    [XmlElement("Bar")] 
    public string BarXml { get { return (Bar) ? "yes" : "no"; } } 
} 

Lưu ý rằng tôi cũng muốn để có thể deserialize dữ liệu này lại một lần nữa.

Trả lời

21

Ok, tôi đã xem xét điều này một số chi tiết.Dưới đây là những gì tôi đã đưa ra:

// use this instead of a bool, and it will serialize to "yes" or "no" 
// minimal example, not very robust 
public struct YesNo : IXmlSerializable { 

    // we're just wrapping a bool 
    private bool Value; 

    // allow implicit casts to/from bool 
    public static implicit operator bool(YesNo yn) { 
     return yn.Value; 
    } 
    public static implicit operator YesNo(bool b) { 
     return new YesNo() {Value = b}; 
    } 

    // implement IXmlSerializable 
    public XmlSchema GetSchema() { return null; } 
    public void ReadXml(XmlReader reader) { 
     Value = (reader.ReadElementContentAsString() == "yes"); 
    } 
    public void WriteXml(XmlWriter writer) { 
     writer.WriteString((Value) ? "yes" : "no"); 
    } 
} 

Sau đó, tôi thay đổi lớp Foo của tôi như thế này:

[XmlRoot()] 
public class Foo {  
    public YesNo Bar { get; set; } 
} 

Lưu ý rằng vì YesNo là ngầm bột nhôm để bool (và ngược lại), bạn vẫn có thể làm điều này:

Foo foo = new Foo() { Bar = true; }; 
if (foo.Bar) { 
    // ... etc 

Nói cách khác, bạn có thể coi nó như một cái bool.

Và w00t! Nó nối tiếp với điều này:

<Foo><Bar>yes</Bar></Foo> 

Nó cũng deserializes chính xác.

Có thể có một số cách để XmlSerializer của tôi tự động truyền bất kỳ số nào bool s nó gặp YesNo khi di chuyển - nhưng tôi chưa tìm thấy. Bất kỳ ai?

+0

Đáng yêu, chỉ là những gì tôi đang tìm kiếm. Cảm ơn. –

+0

bạn có thể làm điều đó như thế quá: enum công BoolEnum { [XmlEnum ("không")] False = 0, [XmlEnum ("yes")] Đúng = 1 } – Sauleil

3

Làm cho giá trị bool serialize là "có" hoặc "không" thay đổi loại dữ liệu từ một boolean. Thay vào đó, bạn có thể thêm thuộc tính riêng để đánh giá boolean và trả về "có" hoặc "không" nếu thích hợp cho loại dữ liệu của nó không? Có lẽ bạn thậm chí có thể buộc "có" hoặc "không" bằng cách làm cho kiểu trả về là một enum chỉ xác định các giá trị đó.

public YesOrNo DoYouLoveIt 
{ 
    get { return boolToEvaluate ? YesOrNo.Yes : YesOrNo.No; } 
} 

Đó có thể là quá mức cần thiết, nhưng có thể trả lời nhu cầu của bạn. Lý do duy nhất tôi đưa ra một enum cho một giá trị đơn giản như vậy là bạn sẽ hạn chế các giá trị so với cho phép bất kỳ chuỗi nào.

+0

Wow ... là "có" quá đơn giản hay gì đó? –

+0

Haha - tôi biết. Đó là vấn đề cho phép bất kỳ chuỗi cũ nào sử dụng loại dữ liệu chuỗi hoặc hạn chế nó thành các giá trị cụ thể. –

+0

: P. Hãy nhớ Enum. (Hãy thử) Parse() là trường hợp nhạy cảm .... –

-1

điều gì về việc triển khai các phương pháp OnSerializing và OnDeserializing?

-1

Những gì bạn cần làm âm thanh giống như vấn đề hiển thị. Nếu ứng dụng của bạn cho phép, bạn sẽ giữ nguyên kiểu dữ liệu dưới dạng boolean và hiển thị Có/Không trong giao diện người dùng của bạn.

+0

Uhh, anh ấy đang nói về việc tuần tự hóa thành XML ... –

+1

"Tôi đang gửi xml đến một chương trình khác" có vẻ như anh ta không kiểm soát chương trình 'khác'. – SCdF

7

Rất đơn giản. Sử dụng một tài sản thay thế. Áp dụng XmlIgnore trên thuộc tính thực tế. Người thay thế là một chuỗi và phải sử dụng thuộc tính XmlElement để ghi đè tên phần tử. Chỉ định tên của thuộc tính thực tế khi ghi đè. Thuộc tính thay thế tuần tự hóa khác nhau dựa trên giá trị của thuộc tính thực tế. Bạn cũng phải cung cấp một setter cho Surrogate, và setter nên thiết lập thuộc tính thực sự một cách thích hợp, cho bất cứ giá trị nào nó được tuần tự hóa. Nói cách khác, nó cần phải đi cả hai cách.

Snip:

public class SomeType 
    { 

     [XmlElement] 
     public int IntValue; 

     [XmlIgnore] 
     public bool Value; 

     [XmlElement("Value")] 
     public string Value_Surrogate { 
      get { return (Value)? "Yes, definitely!":"Absolutely NOT!"; } 
      set { Value= (value=="Yes, definitely!"); } 
     } 

    } 

nhấp chuột vào đây để full compilable source example.

+2

Yup, nhưng đó là ví dụ mà tôi đưa ra trong câu hỏi .. Tôi muốn biết nếu có một cách * tốt hơn * :) – Blorgbeard

+0

Tốt hơn? đừng nghĩ vậy. – Cheeso

0

Ví dụ về tài sản của bạn có lẽ là cách đơn giản nhất bạn có thể thực hiện. Nếu nó giúp, tôi tin rằng bạn không cần phải biến nó thành tài sản công cộng, vì thuộc tính triển khai ISerializable trên lớp đằng sau lưng bạn. Để kích hoạt tính năng deserialization, bạn sẽ chỉ có thể thực hiện set { Bar = value == "yes"; }

+0

Có vẻ như nó không phải là công khai, trừ khi tôi làm điều đó sai [tm] .. – Blorgbeard

2

Tôi sử dụng phương pháp thuộc tính, nhưng thay vì kiểm tra xem chuỗi có bằng hoặc không, tôi muốn kiểm tra xem chuỗi có bắt đầu bằng (không phân biệt dạng chữ) "YT1" hay không. Điều này cho phép tệp chứa đúng, True, t, T, y, Y, có, Có, 1, v.v. tất cả sẽ được đánh giá là đúng. Trong khi tôi có thể xác định rằng sai là false, False, f, F, n, N, không, Không, 0, v.v., bất kỳ thứ gì không khớp với giá trị đúng vẫn được đánh giá là sai.

-1

@Blorgbeard: Nếu bạn có nhiều hơn thì một trong các lớp CóNo này trong lớp đối tượng, đảm bảo đọc toàn bộ phần tử.

public void ReadXml(XmlReader reader) 
{ 
    string element = reader.ReadOuterXml(); 
    int startIndex = element.IndexOf('>') + 1; 
    int length = element.LastIndexOf('<') - startIndex; 

    string text = (element.Substring(startIndex, length).ToLowerInvariant(); 

    Value = (text == "yes"); 
} 

Nếu không, điều này có thể gây ra sự cố.

Phương pháp ReadXml phải đối chiếu đối tượng của bạn bằng thông tin được viết bằng phương pháp WriteXml.

Khi phương thức này được gọi, trình đọc được đặt ở đầu phần tử bao bọc thông tin cho loại của bạn. Tức là, chỉ cần trước thẻ bắt đầu cho biết bắt đầu đối tượng được tuần tự hóa. Khi phương thức này trả về, nó phải đọc toàn bộ phần tử từ đầu đến cuối, bao gồm tất cả nội dung của nó. Không giống như phương thức WriteXml , khung không tự động xử lý phần tử bao bọc . Việc triển khai của bạn phải làm như vậy. Không tuân thủ các quy tắc định vị này có thể gây ra mã để tạo thời gian chạy không mong muốn ngoại lệ hoặc dữ liệu bị hỏng.

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