2009-08-19 29 views
12

OK, tôi thiếu gì ở đây? MSDN nói như sau liên quan đến DateTimeSerializationMode với:Nối tiếp chuỗi XML của DateTime và xsd: date?

Trong các phiên bản 2.0 và sau này của .Net Framework , với tài sản này thiết lập để đối tượng được kiểm tra RoundtripDateTime để xác định xem họ đang ở địa phương, hoặc tính theo giờ UTC một thời gian không xác định khu vực và được tuần tự hóa theo cách như vậy rằng thông tin này được giữ nguyên. Đây là hành vi mặc định và được đề nghị cho tất cả các ứng dụng mới không giao tiếp với các phiên bản cũ hơn của các khung công tác .

Tuy nhiên:

namespace ConsoleApplication1 { 
    public class DateSerTest { 
     [XmlElement(DataType = "date")] 
     public DateTime Date { get; set; } 
    } 

    class Program { 
     static void Main(string[] args) { 
      DateSerTest d = new DateSerTest { 
       Date = DateTime.SpecifyKind(new DateTime(2009,8,18), DateTimeKind.Utc), 
      }; 
      XmlSerializer ser = new XmlSerializer(typeof(DateSerTest)); 
      using (FileStream fs = new FileStream("out.xml", FileMode.Create)) { 
       ser.Serialize(fs, d); 
      } 

      // out.xml will contain: 
      // <Date>2009-08-18</Date> 

      using (FileStream fs = new FileStream("out.xml", FileMode.Open)) { 
       DateSerTest d1 = (DateSerTest) ser.Deserialize(fs); 
       Console.WriteLine(d1.Date); // yields: 8/18/2009 12:00:00 AM 
       Console.WriteLine(d1.Date.Kind); // yields: Unspecified 
      } 

      // in.xml: 
      // <DateSerTest> 
      //  <Date>2009-08-18Z</Date> 
      // </DateSerTest> 

      using (FileStream fs = new FileStream("in.xml", FileMode.Open)) { 
       DateSerTest d1 = (DateSerTest) ser.Deserialize(fs); 
       Console.WriteLine(d1.Date); // yields: 8/17/2009 8:00:00 PM 
       Console.WriteLine(d1.Date.Kind); // yields: Local 
       using (FileStream fs1 = new FileStream("out2.xml", FileMode.Create)) { 
        ser.Serialize(fs1, d1); 

        // out2.xml will contain: 
        // <Date>2009-08-17</Date> 
       } 
      } 
      Console.ReadKey(); 
     } 
    } 
} 

Vì vậy, cho các yếu tố XSD định nghĩa là "ngày" chứ không phải "datetime", ngày được không đăng như UTC. Đây là một vấn đề, bởi vì nếu tôi deserialize XML này ngày kết quả sẽ là Kind Unspecified, và bất kỳ chuyển đổi UTC (mà trong thực tế sẽ là một no-op vì UTC-ness của ngày nên được bảo quản trong suốt chuyến đi), sẽ thay đổi ít nhất là thời gian trong ngày, với 50% cơ hội thực hiện ngày hôm qua, tùy thuộc vào việc bạn đang ở phía đông hay phía tây của Greenwich.

nên không ngày được viết như sau:

<Date>2009-08-18Z</Date> 

? Thật vậy, nếu tôi deserialize một tài liệu có chứa ở trên, tôi nhận được một DateTime đã được chuyển đổi sang thời gian địa phương (Tôi đang ở New York vì vậy đó là ngày 17 tháng 8 20:00), và nếu tôi ngay lập tức serialize đối tượng đó trở về XML, tôi nhận được:

<Date>2009-08-17</Date> 

Vì vậy, UTC đã được chuyển đổi sang địa phương trên đường vào, và một phần thời gian đó địa phương đã giảm trên đường ra, mà sẽ làm cho nó Unspecified trên đường trở lại một lần nữa . Chúng tôi đã mất tất cả kiến ​​thức về đặc điểm kỹ thuật ngày UTC ban đầu của ngày 18 tháng 8.

Đây là những gì W3C nói về xsd: date:

[Định nghĩa:] Cú pháp · không gian giá trị · của ngày bao gồm khoảng thời gian đầu mở của đúng một ngày dài trên mốc thời gian của dateTime, bắt đầu từ thời điểm bắt đầu mỗi ngày (trong mỗi múi giờ), tức là '00: 00: 00 ', tối đa nhưng không bao gồm '24: 00: 00' (là giống hệt với '00: 00 : 00 'của ngày tiếp theo ngày). Đối với các giá trị không được định thời gian, các khoảng thời gian mở hàng đầu là tách rời dòng thời gian không được định trước, mỗi lần ngày. Đối với các giá trị thời gian, các khoảng thời gian bắt đầu vào mỗi phút và do đó trùng lặp.

Các vấn đề cơ bản là nếu tôi làm như sau:

  1. Thi công (hoặc nhận) một giá trị tính theo giờ UTC DateTime.
  2. Nối tiếp với XML bằng lược đồ xác định trường đó là xsd: date
  3. Deserialize XML đó trở lại DateTime.
  4. Chuyển đổi DateTime thành UTC (sẽ không có hiệu lực vì "roundtrip" nên đã được giữ nguyên này).

Hoặc như sau:

  1. Deserialize một tài liệu XML chứa một UTC xsd: (. Ví dụ 2009-08-18Z) ngày đối tượng.
  2. Sắp xếp lại nó trở lại tài liệu XML mới mà không cần chạm vào nó.

Một trong các thủ tục này nên làm cho tôi cùng ngày tôi đưa vào.

Cách giải quyết

Cách duy nhất tôi có thể thấy cho đến nay để có được những khứ hồi hành vi của tôi mong đợi là để triển khai thuộc tính Date như sau, với giả định rằng tất cả các phần tử xsd: date đại diện cho UTC:

[XmlElement(DataType = "date")] 
public DateTime Date { 
    get { return _dt; } 
    set { _dt = value.Kind == DateTimeKind.Unspecified ? 
        DateTime.SpecifyKind(value, DateTimeKind.Utc) : 
        value.ToUniversalTime(); } 
} 
+0

Điều nhỏ: Trình nối tiếp XML không sử dụng '[Serializable]'. –

+0

Ok, với sửa đổi của bạn, tôi không còn thấy câu hỏi. Câu hỏi là gì? – Cheeso

+1

Tôi vẫn đang cố gắng thu hút đầu óc, nhưng tôi đoán câu hỏi của tôi vào thời điểm này là: Tôi có quyền kết luận rằng chuỗi tuần tự hóa XML giữa DateTime và xsd: date bị hỏng không? – lesscode

Trả lời

8

Tôi mở một vấn đề Connect và đã trở lại này từ Microsoft, xác nhận lo ngại của tôi:

Chúng tôi có các hành vi khác nhau cho xử lý Ngày, Thời gian và Ngày giờ giá trị. Đối với các giá trị Ngày giờ, nếu XmlDateTimeSerializationMode không phải là Địa phương thông tin về loại (UTC, địa phương hoặc không xác định) là được giữ nguyên. Điều này cũng đúng trong khi deserializing. Tuy nhiên, đối với Ngày và Thời gian, chúng luôn được sắp xếp theo thứ tự với cùng định dạng: (yyyy-MM-dd cho Ngày và HH: mm: ss.fffffff.zzzzzz cho Thời gian). Vì vậy, các thông tin về loại bị mất khi tuần tự hóa và deserializing. Chúng tôi đang mở một lỗi tài liệu về phía chúng tôi theo thứ tự để cải thiện tài liệu về điều này.

+1

ai biết nếu điều này đã từng được giải quyết? –

+2

Phụ thuộc vào ý của bạn bằng cách 'giải quyết' ... Tuyên bố của Microsoft cho tôi biết rằng API không làm điều đúng, nhưng họ sẽ 'giải quyết' nó bằng cách ghi lại sự bất thường. – lesscode

+0

Vì vậy, bạn có thể chỉ cần xác định các tài sản được serialized như DateTime để giải quyết vấn đề sau đó? – xr280xr

1

Tôi không thấy vấn đề bạn mô tả.

Mã mẫu của bạn không deserialize. Tôi đã thêm một số mã để deserialize, và nó roundtrips như tôi mong đợi. Tôi không thấy ngày di chuyển trở lại một ngày, hoặc chuyển tiếp một ngày.

Tôi đã thông báo rằng phần thời gian của trường d.Date bị tước để tuần tự hóa, bất kể DateTimeKind là gì. Điều này có vẻ đúng với tôi. Nó không có ý nghĩa với tôi, một cách trực quan, hoặc là sắp xếp một múi giờ với một "Ngày", hoặc để chuyển đổi sang UTC. Tôi sẽ ngạc nhiên rằng nếu tôi có giá trị ngày tháng 8-18-2009, và khi được tuần tự hóa, nó xuất hiện ở dạng 8-19-2009Z. Vì vậy, tôi nghĩ cách nó hoạt động bây giờ có vẻ đúng.

  • Ngày giờ được sắp xếp theo xsd: dateTime bao gồm thông tin vùng.
  • Ngày giờ được tuần tự hóa dưới dạng xsd: ngày, không. Tôi cũng hy vọng rằng với [XmlElement(DateType="time")] (xsd: thời gian), múi giờ sẽ không được bao gồm. Tôi đã không kiểm tra điều này.

Vì vậy, vấn đề như tôi thấy, hành vi này, có ý nghĩa với tôi, không được ghi chép rõ ràng, đặc biệt là với những thay đổi được giới thiệu để làm tròn. Thực tế là DataType = "date" và DataType = "time" không chuyển đổi sang UTC để tuần tự hóa, cần được nêu rõ.

bạn đã viết:

và bất kỳ chuyển đổi sang UTC sẽ thay đổi ít nhất thời gian trong ngày,

Nhưng tôi không thấy điều này ở tất cả. Khi tôi chuyển đổi một thời gian là DateTimeKind.Unspecified, thành Utc, nó không thay đổi thời gian trong ngày. Nó chỉ thay đổi loại.

class Program 
{ 
    static System.IO.MemoryStream StringToMemoryStream(string s) 
    { 
     byte[] a = System.Text.Encoding.ASCII.GetBytes(s); 
     return new System.IO.MemoryStream(a); 
    } 


    static void Main(string[] args) 
    { 
     var settings = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true }; 
     XmlSerializerNamespaces _ns = new XmlSerializerNamespaces(); 
     _ns.Add("", ""); 

     Console.WriteLine("\nDate Serialization testing..."); 

     for (int m=0; m < 2; m++) 
     { 
      var builder = new System.Text.StringBuilder(); 

      DateTime t = DateTime.Parse("2009-08-18T22:31:24.0019-04:00"); 
      DateSerTest d = new DateSerTest 
       { 
        Date = t, 
        DateTime = t 
       }; 

      Console.WriteLine("\nRound {0}", m+1); 
      if (m==1) 
       d.Date = d.Date.ToUniversalTime(); 

      Console.WriteLine("d.Date {2,-11} = {0} Kind({1})", d.Date.ToString("u"), d.Date.Kind.ToString(), 
           (m==1) ? "(converted)" : "(original)"); 
      Console.WriteLine("d.DateTime   = {0} Kind({1})", d.DateTime.ToString("u"), d.DateTime.Kind.ToString()); 

      XmlSerializer ser = new XmlSerializer(typeof(DateSerTest)); 

      Console.WriteLine("\nSerialize d"); 
      using (var writer = System.Xml.XmlWriter.Create(builder, settings)) 
      { 
       ser.Serialize(writer, d, _ns); 
      } 
      string xml = builder.ToString(); 
      Console.WriteLine("{0}", xml); 

      Console.WriteLine("\nDeserialize into d2"); 
      System.IO.MemoryStream ms = StringToMemoryStream(xml); 
      DateSerTest d2= (DateSerTest) ser.Deserialize(ms); 

      Console.WriteLine("d2.Date = {0} Kind({1})", d2.Date.ToString("u"), d2.Date.Kind.ToString()); 
      Console.WriteLine("d2.DateTime= {0} Kind({1})", d2.DateTime.ToString("u"), d2.DateTime.Kind.ToString()); 

      Console.WriteLine("\nAfter SpecifyKind"); 
      d2.Date = DateTime.SpecifyKind(d2.Date, DateTimeKind.Utc); 
      Console.WriteLine("d2.Date = {0} Kind({1})", d2.Date.ToString("u"), d2.Date.Kind.ToString()); 

      Console.WriteLine("\nRe-Serialize d2"); 
      builder = new System.Text.StringBuilder(); 
      using (var writer = System.Xml.XmlWriter.Create(builder, settings)) 
      { 
       ser.Serialize(writer, d2, _ns); 
      } 
      xml = builder.ToString(); 
      Console.WriteLine("{0}", xml); 

     } 
    } 
} 

Kết quả:

 

    Date Serialization testing... 

    Round 1 
    d.Date (original) = 2009-08-18 22:31:24Z Kind(Local) 
    d.DateTime   = 2009-08-18 22:31:24Z Kind(Local) 

    Serialize d 
    <DateSerTest> 
     <Date>2009-08-18</Date> 
     <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> 
    </DateSerTest> 

    Deserialize into d2 
    d2.Date = 2009-08-18 00:00:00Z Kind(Unspecified) 
    d2.DateTime= 2009-08-18 22:31:24Z Kind(Local) 

    After SpecifyKind 
    d2.Date = 2009-08-18 00:00:00Z Kind(Utc) 

    Re-Serialize d2 
    <DateSerTest> 
     <Date>2009-08-18</Date> 
     <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> 
    </DateSerTest> 

    Round 2 
    d.Date (converted) = 2009-08-19 02:31:24Z Kind(Utc) 
    d.DateTime   = 2009-08-18 22:31:24Z Kind(Local) 

    Serialize d 
    <DateSerTest> 
     <Date>2009-08-19</Date> 
     <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> 
    </DateSerTest> 

    Deserialize into d2 
    d2.Date = 2009-08-19 00:00:00Z Kind(Unspecified) 
    d2.DateTime= 2009-08-18 22:31:24Z Kind(Local) 

    After SpecifyKind 
    d2.Date = 2009-08-19 00:00:00Z Kind(Utc) 

    Re-Serialize d2 
    <DateSerTest> 
     <Date>2009-08-19</Date> 
     <DateTime>2009-08-18T22:31:24.0019-04:00</DateTime> 
    </DateSerTest> 
+0

Bạn nói đúng là vấn đề đang được sắp xếp theo thứ tự ngày UTC - đó là lý do tại sao tôi đã xóa mẫu của mình thành chỉ - tôi đã thêm phần còn lại. – lesscode

+0

DateTimes Unspecified làm thực sự được chuyển đổi như thể chúng là địa phương, theo MSDN: DateTime.ToUniversalTime Phương pháp ... Unspecified Các đối tượng DateTime hiện được giả định là một giờ địa phương, và việc chuyển đổi được thực hiện như nếu Kind là Local. – lesscode

+0

SpecifyKind, tất nhiên, không thay đổi thời gian hoặc ngày tháng. – lesscode

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