2012-02-21 16 views
12

Tôi đang cố gắng sắp xếp một đối tượng với một số thuộc tính, nhưng tôi không muốn bao gồm tất cả các thuộc tính trong tuần tự hóa. Ngoài ra, tôi muốn thay đổi định dạng ngày.Loại trừ một số thuộc tính trong quá trình tuần tự mà không thay đổi lớp gốc

Tất nhiên tôi có thể thêm [XmlIgnore], nhưng tôi không được phép thay đổi lớp gốc.

Tùy chọn duy nhất tôi có thể nghĩ là tạo một lớp mới và sao chép tất cả nội dung giữa hai lớp. Nhưng đó sẽ là xấu xí và sẽ đòi hỏi rất nhiều mã thủ công.

Có thể tạo một lớp con, vì bản gốc không trừu tượng?

Câu hỏi của tôi là như sau:

  1. Làm thế nào tôi có thể loại trừ một số thuộc tính mà không thay đổi lớp ban đầu?

  2. Làm cách nào để tùy chỉnh định dạng ngày của XML đầu ra?

Yêu cầu:

  1. gõ Như mạnh càng tốt

  2. In nhiều XML nên deserializable

Cảm ơn trước.

+0

Btw, chúng tôi đang sử dụng .NET 4.0 – Schiavini

+0

Và XmlSerializer – Schiavini

Trả lời

11

Đối với bất cứ ai quan tâm, tôi quyết định sử dụng XmlAttributeOverrides, nhưng làm cho họ đánh máy mạnh mẽ hơn (Tôi ghét phải gõ tên tài sản như dây đàn). Dưới đây là phương pháp mở rộng Tôi đã từng cho nó:

public static void Add<T>(this XmlAttributeOverrides overrides, Expression<Func<T, dynamic>> propertySelector, XmlAttributes attributes) 
    { 
     overrides.Add(typeof(T), propertySelector.BuildString(), attributes); 
    } 

    public static string BuildString(this Expression propertySelector) 
    { 
     switch (propertySelector.NodeType) 
     { 
      case ExpressionType.Lambda: 
       LambdaExpression lambdaExpression = (LambdaExpression)propertySelector; 
       return BuildString(lambdaExpression.Body); 

      case ExpressionType.Convert: 
      case ExpressionType.Quote: 
       UnaryExpression unaryExpression = (UnaryExpression)propertySelector; 
       return BuildString(unaryExpression.Operand); 

      case ExpressionType.MemberAccess: 

       MemberExpression memberExpression = (MemberExpression)propertySelector; 
       MemberInfo propertyInfo = memberExpression.Member; 

       if (memberExpression.Expression is ParameterExpression) 
       { 
        return propertyInfo.Name; 
       } 
       else 
       { 
        // we've got a nested property (e.g. MyType.SomeProperty.SomeNestedProperty) 
        return BuildString(memberExpression.Expression) + "." + propertyInfo.Name; 
       } 

      default: 
       // drop out and throw 
       break; 
     } 
     throw new InvalidOperationException("Expression must be a member expression: " + propertySelector.ToString()); 
    } 

Sau đó, để bỏ qua một thuộc tính, tôi có thể đẹp thêm nó vào danh sách bỏ qua:

var overrides = new XmlAttributeOverrides(); 
    var ignore = new XmlAttributes { XmlIgnore = true }; 
    overrides.Add<MyClass>(m => m.Id, ignore); 
    overrides.Add<MyClass>(m => m.DateChanged, ignore); 
    Type t = typeof(List<MyClass>); 
    XmlSerializer serial = new XmlSerializer(t, overrides); 
+3

Bất kỳ 'XmlSerializer' nào được xây dựng bằng cách sử dụng' XmlAttributeOverrides' chỉ nên được xây dựng một lần, sau đó được lưu trữ để sử dụng lại sau này. Để được giải thích, hãy xem [Memory Leak sử dụng StreamReader và XmlSerializer] (https://stackoverflow.com/questions/23897145) và [XmlSerializer extraTypes memory leak] (https://stackoverflow.com/questions/38892352). – dbc

3

Nếu bạn đang sử dụng XmlSerializer, XmlAttributeOverrides có lẽ là những gì bạn cần.

Cập nhật: Tôi đã nghiên cứu khả năng tùy chỉnh định dạng ngày và, theo như tôi thấy, không có giải pháp đẹp nào tồn tại.

Một tùy chọn, như đã được đề cập bởi những người khác, là triển khai IXmlSerializable. Điều này có nhược điểm là bạn hoàn toàn chịu trách nhiệm (de-) serializing toàn bộ đối tượng (-graph).

Tùy chọn thứ hai, với danh sách khá nhiều hạn chế, là phân lớp lớp cơ sở (bạn đã đề cập đến nó như là một thay thế trong bài đăng của bạn). Với một số hệ thống ống nước khá, chuyển đổi từ và đến các đối tượng ban đầu, và việc sử dụng các XmlAttributeOverrides bạn có thể xây dựng một cái gì đó như thế này:

public class Test 
{ 
    public int Prop { get; set; } 
    public DateTime TheDate { get; set; } 
} 

public class SubTest : Test 
{ 
    private string _customizedDate; 
    public string CustomizedDate 
    { 
     get { return TheDate.ToString("yyyyMMdd"); } 
     set 
     { 
      _customizedDate = value; 
      TheDate = DateTime.ParseExact(_customizedDate, "yyyyMMdd", null); 
     } 
    } 

    public Test Convert() 
    { 
     return new Test() { Prop = this.Prop }; 
    } 
} 

// Serialize 
XmlAttributeOverrides overrides = new XmlAttributeOverrides(); 
XmlAttributes attributes = new XmlAttributes(); 
attributes.XmlIgnore = true; 
overrides.Add(typeof(Test), "TheDate", attributes); 

XmlSerializer xs = new XmlSerializer(typeof(SubTest), overrides); 
SubTest t = new SubTest() { Prop = 10, TheDate = DateTime.Now, CustomizedDate="20120221" }; 
xs.Serialize(fs, t); 

// Deserialize 
XmlSerializer xs = new XmlSerializer(typeof(SubTest)); 
SubTest t = (SubTest)xs.Deserialize(fs); 
Test test = t.Convert(); 

Nó không phải là đẹp, nhưng nó sẽ làm việc.

Lưu ý rằng bạn đang thực sự (de-) tuần tự hóa các đối tượng SubTest trong trường hợp này. Nếu loại chính xác là quan trọng, thì đây cũng không phải là một lựa chọn.

7

Bạn có thể loại trừ một số thuộc tính bằng cách tận dụng thực tế là XmlSerializer sẽ không tuần tự hóa null cho đầu ra. Vì vậy, đối với các loại tham chiếu, bạn có thể vô hiệu hóa các thuộc tính mà bạn không muốn xuất hiện trong xml.

Kết quả xml sẽ được deserializable trở lại vào cùng một lớp, nhưng các trường bỏ qua rõ ràng sẽ là null.

Tuy nhiên, điều này không giúp bạn thay đổi định dạng ngày. Đối với điều này, bạn sẽ cần tạo một lớp mới có ngày dưới dạng chuỗi theo định dạng bạn muốn hoặc bạn có thể triển khai IXmlSerializable, cho phép bạn hoàn toàn kiểm soát xml. [cần lưu ý rằng kiểu dữ liệu ngày tháng có định dạng chuẩn trong XML, do đó, bằng cách thay đổi nó sẽ không còn là một ngày XML nữa - bạn có thể không quan tâm].

[EDIT để đáp lại ý kiến ​​của bạn]

Có một mẹo bổ sung mà bạn có thể sử dụng để "biến mất" một loại nullable null, nhưng nó đòi hỏi một sự thay đổi lớp học của bạn. Bộ nối tiếp, khi tuần tự hóa MyProperty - cũng sẽ kiểm tra xem có thuộc tính được gọi là MyProperySpecified hay không. Nếu nó tồn tại và trả về false, thuộc tính item không được serialized:

public class Person 
{ 
    [XmlElement] 
    public string Name { get; set; } 

    [XmlElement] 
    public DateTime? BirthDate { get; set; } 

    public bool BirthDateSpecified 
    { 
     get { return BirthDate.HasValue; } 
    } 
} 

Nếu bạn chuẩn bị thêm thuộc tính này, bạn có thể loại bỏ các loại null khi null. Trong thực tế - bây giờ tôi nghĩ về nó - đây có thể là một cách hữu ích để loại bỏ các thuộc tính khác quá, tùy thuộc vào kịch bản sử dụng của bạn.

+0

Khi thuộc tính của tôi là null, họ nhận được thẻ Schiavini

+1

Bạn đã đánh dấu chúng là 'IsNullable' - nếu vậy, giá trị null sẽ được tuần tự hóa với' xsi : nil = "true" 'thay vì bỏ qua chúng. –

+0

Một số trong số đó là 'DateTime?' Hoặc 'int?', Nhưng cũng có các chuỗi .. – Schiavini

2

Tùy chọn đầu tiên là sử dụng lớp XmlAttributeOverrides.

Hoặc bạn có thể thử để tạo ra lớp được thừa kế và thực hiện IXmlSerializable interface

public class Program 
{ 
    static void Main(string[] args) 
    { 
     StringWriter sr1 = new StringWriter(); 
     var baseSerializer = new XmlSerializer(typeof(Human)); 
     var human = new Human {Age = 30, Continent = Continent.America}; 
     baseSerializer.Serialize(sr1, human); 
     Console.WriteLine(sr1.ToString()); 
     Console.WriteLine(); 

     StringWriter sr2 = new StringWriter(); 
     var specialSerializer = new XmlSerializer(typeof(SpecialHuman)); 
     var special = new SpecialHuman() {Age = 40, Continent = Continent.Africa}; 
     specialSerializer.Serialize(sr2, special); 
     Console.WriteLine(sr2.ToString()); 
     Console.ReadLine(); 
    } 

    public enum Continent 
    { 
     Europe, 
     America, 
     Africa 
    } 

    public class Human 
    { 
     public int Age { get; set; } 
     public Continent Continent { get; set; } 
    } 

    [XmlRoot("Human")] 
    public class SpecialHuman : Human, IXmlSerializable 
    { 
     #region Implementation of IXmlSerializable 

     /// <summary> 
     /// This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/> to the class. 
     /// </summary> 
     /// <returns> 
     /// An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/> method. 
     /// </returns> 
     public XmlSchema GetSchema() 
     { 
      throw new NotImplementedException(); 
     } 

     public void ReadXml(XmlReader reader) 
     { 
      throw new NotImplementedException(); 
     } 

     public void WriteXml(XmlWriter writer) 
     { 
      writer.WriteElementString("Age", Age.ToString()); 
      switch(Continent) 
      { 
       case Continent.Europe: 
       case Continent.America: 
        writer.WriteElementString("Continent", this.Continent.ToString()); 
        break; 
       case Continent.Africa: 
        break; 
       default: 
        throw new ArgumentOutOfRangeException(); 
      } 
     } 

     #endregion 
    } 

} 
+0

Điều này có vẻ tốt nhưng với nhiều thuộc tính vẫn yêu cầu phải có rất nhiều mã thủ công .. – Schiavini

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