2009-10-07 9 views
27

Tôi serializing một lớp học như thế nàyLàm thế nào để loại trừ thuộc tính null khi sử dụng XmlSerializer

public MyClass 
{ 
    public int? a { get; set; } 
    public int? b { get; set; } 
    public int? c { get; set; } 
} 

Tất cả các loại là nullable vì tôi muốn dữ liệu tối thiểu được lưu trữ khi tuần tự một đối tượng thuộc loại này. Tuy nhiên, khi nó được đăng chỉ với "một" dân cư, tôi nhận được xml sau

<MyClass ...> 
    <a>3</a> 
    <b xsi:nil="true" /> 
    <c xsi:nil="true" /> 
</MyClass> 

Làm thế nào để thiết lập này lên đến chỉ nhận được xml cho các thuộc tính rỗng không? Kết quả mong muốn sẽ là

<MyClass ...> 
    <a>3</a> 
</MyClass> 

Tôi muốn loại trừ các giá trị null vì sẽ có một số thuộc tính và điều này là nhận được lưu trữ trong một cơ sở dữ liệu (yeah, thats không phải cuộc gọi của tôi) vì vậy tôi muốn giữ lại các dữ liệu chưa sử dụng tối thiểu .

+3

Nếu bạn đã thêm tất cả thời gian phát triển chất thải cố gắng để có được xml để xem cách họ * nghĩ * nó nên nhìn ... bạn sẽ có một crapton toàn bộ giờ phát triển. Tôi đã từ bỏ lâu rồi. Bạn nên xem đó là một lựa chọn. – Will

+1

@ Sẽ, tôi thường sẽ, không có vấn đề gì cả, nhưng điều này sẽ được sử dụng hàng ngàn lần một ngày và cả lớp, được tuần tự hóa, là khoảng 1000 ký tự, thats nếu tất cả các thuộc tính là null! Ngoài ra, tất cả điều này là trong db, không phải là sự lựa chọn của tôi: ( –

+3

Đây là một câu hỏi hay, nhưng tôi nghĩ rằng đó là một bản sao của http://stackoverflow.com/questions/1296468/suppress-null-value-types-from -being-emitted-by-xmlserializer (mà Marc Gravell trả lời bằng cách thảo luận về mô hình đặc điểm) –

Trả lời

6

Tôi cho rằng bạn có thể tạo XmlWriter để lọc ra tất cả các phần tử có thuộc tính xsi: nil và chuyển tất cả các cuộc gọi khác đến nhà văn thực sự bên dưới.

+0

Tôi thích nó, rất đơn giản, ý tưởng tuyệt vời :) –

+1

Tôi đã đăng việc triển khai thực hiện điều này (cũng như tùy chọn bỏ qua các không gian tên) tại https://plus.google.com/+JoelElliott27/posts/ePPuwnH5Za8 – Abacus

+0

@Abacus cảm ơn bài đăng của bạn, nhưng nó không xóa phần tử hoàn chỉnh. Hay tôi đang thiếu một cái gì đó? – ArieKanarie

28

Bạn bỏ qua các yếu tố cụ thể với specification

public MyClass 
{ 
    public int? a { get; set; } 

    [System.Xml.Serialization.XmlIgnore] 
    public bool aSpecified { get { return this.a != null; } } 

    public int? b { get; set; } 
    [System.Xml.Serialization.XmlIgnore] 
    public bool bSpecified { get { return this.b != null; } } 

    public int? c { get; set; } 
    [System.Xml.Serialization.XmlIgnore] 
    public bool cSpecified { get { return this.c != null; } } 
} 

Các {} lĩnh vực bất động sản rõ sẽ cho serializer nếu nó nên serialize các trường tương ứng hay không bằng cách trả lại đúng/sai.

+2

lưu ý: Tôi trả lời điều này bởi vì thành thực, tôi đang tìm một giải pháp tốt hơn, tôi không phải là một fan hâm mộ của tất cả các lĩnh vực phụ như lớp học của tôi có các lĩnh vực SEVERAL để serialize –

+2

Tôi có thể đã hiểu lầm 'tiền thưởng của bạn', nhưng null chuỗi sẽ tự động bỏ qua, mà không có xsi: nil = "true" flotsam –

+0

@ Jeff, Ah, do đó, họ là :-P Cảm ơn –

0

Cách đơn giản nhất của viết code như thế này mà đầu ra chính xác là rất quan trọng là:

  1. Viết một Schema XML mô tả định dạng mong muốn chính xác của bạn.
  2. Chuyển đổi giản đồ của bạn thành một lớp bằng cách sử dụng xsd.exe.
  3. Chuyển đổi lớp của bạn trở lại lược đồ (sử dụng xsd.exe lần nữa) và kiểm tra lược đồ của bạn để đảm bảo rằng trình tuần tự sao chép chính xác mọi hành vi bạn muốn.

Tinh chỉnh và lặp lại cho đến khi bạn có mã làm việc.

Nếu bạn không chắc chắn về loại dữ liệu chính xác để sử dụng ban đầu, hãy bắt đầu với bước 3 thay vì bước 1, sau đó tinh chỉnh.

IIRC, cho ví dụ của bạn, bạn gần như chắc chắn sẽ kết thúc với Specified thuộc tính như bạn đã mô tả, nhưng để chúng được tạo cho bạn chắc chắn nhịp đập bằng tay. :-)

-2

Đánh dấu phần tử bằng [XmlElement ("elementName", IsNullable = false)] giá trị null sẽ bị bỏ qua.

+1

Điều này sẽ không hoạt động. 'XmlSerializer' ném ra một ngoại lệ khi một thuộc tính của kiểu' int? 'Được định nghĩa như thế này. –

3

Tốt hơn không bao giờ ...

Tôi tìm thấy một cách (có thể chỉ có sẵn với khung mới nhất mà tôi không biết) để làm điều này. Tôi đã sử dụng DataMember thuộc tính cho một hợp đồng WCF webservice và tôi đánh dấu đối tượng của tôi như thế này:

[DataMember(EmitDefaultValue = false)] 
public decimal? RentPrice { get; set; } 
+0

DataMember sống trong không gian tên System.Runtime.Serialization trong khi svc serializer sử dụng System.Xml.Serialization. điều này sẽ không hoạt động trừ khi bạn sử dụng serializer khác. –

+0

Vâng, điều này sẽ chỉ làm việc với DataContractSerializer, không phải XmlSerializer – crush

2

Tuy nhiên, giải pháp khác: regex để giải cứu, sử dụng \s+<\w+ xsi:nil="true" \/> để loại bỏ tất cả các thuộc tính null từ một chuỗi chứa XML. Tôi đồng ý, không phải là giải pháp thanh lịch nhất và chỉ hoạt động nếu bạn chỉ phải tuần tự hóa. Nhưng đó là tất cả những gì tôi cần hôm nay, và tôi không muốn thêm các thuộc tính {Foo}Specified cho tất cả các thuộc tính không có giá trị.

public string ToXml() 
{ 
    string result; 

    var serializer = new XmlSerializer(this.GetType()); 

    using (var writer = new StringWriter()) 
    { 
     serializer.Serialize(writer, this); 
     result = writer.ToString(); 
    } 

    serializer = null; 

    // Replace all nullable fields, other solution would be to use add PropSpecified property for all properties that are not strings 
    result = Regex.Replace(result, "\\s+<\\w+ xsi:nil=\"true\" \\/>", string.Empty); 

    return result; 
} 
2

1) Mở rộng

public static string Serialize<T>(this T value) { 
     if (value == null) { 
      return string.Empty; 
     } 
     try { 
      var xmlserializer = new XmlSerializer(typeof(T)); 
      var stringWriter = new Utf8StringWriter(); 
      using (var writer = XmlWriter.Create(stringWriter)) { 
       xmlserializer.Serialize(writer, value); 
       return stringWriter.ToString(); 
      } 
     } catch (Exception ex) { 
      throw new Exception("An error occurred", ex); 
     } 
    } 

1a) Utf8StringWriter

public class Utf8StringWriter : StringWriter { 
    public override Encoding Encoding { get { return Encoding.UTF8; } } 
} 

2) Tạo XElement

XElement xml = XElement.Parse(objectToSerialization.Serialize()); 

3) Hủy bỏ Nil của

xml.Descendants().Where(x => x.Value.IsNullOrEmpty() && x.Attributes().Where(y => y.Name.LocalName == "nil" && y.Value == "true").Count() > 0).Remove(); 

4) Lưu để nộp

xml.Save(xmlFilePath); 
2

Câu hỏi này đã được hỏi một thời gian khá lâu rồi nhưng vẫn là RẤT có liên quan ngay cả trong năm 2017. Không ai trong số các câu trả lời đưa ra ở đây là không thỏa đáng đối với tôi vì vậy đây là một giải pháp đơn giản tôi đã đưa ra:

Sử dụng cụm từ thông dụng là chìa khóa. Vì chúng ta không kiểm soát nhiều hành vi của XmlSerializer, vì vậy chúng ta đừng cố gắng ngăn chặn nó khỏi việc tuần tự hóa các kiểu giá trị nullable này. Thay vào đó, chỉ cần lấy đầu ra tuần tự và thay thế các phần tử không mong muốn bằng một chuỗi rỗng bằng cách sử dụng Regex. Các mô hình được sử dụng (trong C#) là:

<\w+\s+\w+:nil="true"(\s+xmlns:\w+="http://www.w3.org/2001/XMLSchema-instance")?\s*/> 

Dưới đây là một ví dụ:

using System.IO; 
using System.Text; 
using System.Text.RegularExpressions; 
using System.Xml; 
using System.Xml.Serialization; 

namespace MyNamespace 
{ 
    /// <summary> 
    /// Provides extension methods for XML-related operations. 
    /// </summary> 
    public static class XmlSerializerExtension 
    { 
     /// <summary> 
     /// Serializes the specified object and returns the XML document as a string. 
     /// </summary> 
     /// <param name="obj">The object to serialize.</param> 
     /// <param name="namespaces">The <see cref="XmlSerializerNamespaces"/> referenced by the object.</param> 
     /// <returns>An XML string that represents the serialized object.</returns> 
     public static string Serialize(this object obj, XmlSerializerNamespaces namespaces = null) 
     { 
      var xser = new XmlSerializer(obj.GetType()); 
      var sb = new StringBuilder(); 

      using (var sw = new StringWriter(sb)) 
      { 
       using (var xtw = new XmlTextWriter(sw)) 
       { 
        if (namespaces == null) 
         xser.Serialize(xtw, obj); 
        else 
         xser.Serialize(xtw, obj, namespaces); 
       } 
      } 

      return sb.ToString().StripNullableEmptyXmlElements(); 
     } 

     /// <summary> 
     /// Removes all empty XML elements that are marked with the nil="true" attribute. 
     /// </summary> 
     /// <param name="input">The input for which to replace the content. </param> 
     /// <param name="compactOutput">true to make the output more compact, if indentation was used; otherwise, false.</param> 
     /// <returns>A cleansed string.</returns> 
     public static string StripNullableEmptyXmlElements(this string input, bool compactOutput = false) 
     { 
      const RegexOptions OPTIONS = 
      RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline; 

      var result = Regex.Replace(
       input, 
       @"<\w+\s+\w+:nil=""true""(\s+xmlns:\w+=""http://www.w3.org/2001/XMLSchema-instance"")?\s*/>", 
       string.Empty, 
       OPTIONS 
      ); 

      if (compactOutput) 
      { 
       var sb = new StringBuilder(); 

       using (var sr = new StringReader(result)) 
       { 
        string ln; 

        while ((ln = sr.ReadLine()) != null) 
        { 
         if (!string.IsNullOrWhiteSpace(ln)) 
         { 
          sb.AppendLine(ln); 
         } 
        } 
       } 

       result = sb.ToString(); 
      } 

      return result; 
     } 
    } 
} 

Tôi hy vọng điều này sẽ giúp.