2009-04-24 82 views
3

我有一個表示信用卡詳細信息的類。爲了表示從和過期幾個月甚至幾年有效,我使用int類型的四個屬性:使用XML序列化格式化元素/屬性值

public int ValidFromMonth { get; set; } 
public int ValidFromYear { get; set; } 
public int ExpiresEndMonth { get; set; } 
public int ExpiresEndYear { get; set; } 

我XML由第三方序列化這個類消費。第三方需要我的月份和年份值與前導零作爲前綴,如果該值小於10

<validFromMonth>02</validFromMonth> 
<validFromYear>09</validFromYear> 
<expiresEndMonth>10</expiresEndMonth> 
<expiresEndYear>14</expiresEndYear> 

是否.NET支持的任何屬性(或者是我能夠創建一個自定義屬性)將執行此規則,可能使用格式字符串(例如{0:00})?

注:我知道我可以添加自己的string性能在內部做了格式化,並添加[XmlIgnore]屬性我int屬性,但這種感覺就像一個二流的解決方案。

編輯: 經過一些考慮,我想知道這實際上是不可行的。序列化不成問題,但爲了反序列化工作,您需要取消格式化序列化的字符串。在上面這個簡單的例子中,這很容易,但我不確定是否可以在更一般的情況下工作。

編輯2: 定義兩位數要求的XML架構如下。

簡單類型定義:

使用這些類型的
<xs:simpleType name="CreditCardMonthType"> 
    <xs:annotation> 
    <xs:documentation>Two digit month</xs:documentation> 
    </xs:annotation> 
    <xs:restriction base="xs:string"> 
    <xs:minLength value="2" /> 
    <xs:maxLength value="2" /> 
    </xs:restriction> 
</xs:simpleType> 
<xs:simpleType name="CreditCardYearType"> 
    <xs:annotation> 
    <xs:documentation>Two digit year</xs:documentation> 
    </xs:annotation> 
    <xs:restriction base="xs:string"> 
    <xs:minLength value="2" /> 
    <xs:maxLength value="2" /> 
    </xs:restriction> 
</xs:simpleType> 

信用卡的定義:

<xs:attribute name="ExpiryMonth" type="CreditCardMonthType" use="required"> 
<xs:annotation> 
    <xs:documentation>Credit/debt card's expiry month.</xs:documentation> 
</xs:annotation> 
</xs:attribute> 
<xs:attribute name="ExpiryYear" type="CreditCardYearType" use="required"> 
<xs:annotation> 
    <xs:documentation>Credit/debt card's expiry year.</xs:documentation> 
</xs:annotation> 
</xs:attribute> 
<xs:attribute name="StartMonth" type="CreditCardMonthType" use="optional"> 
<xs:annotation> 
    <xs:documentation>Switch card's start month.</xs:documentation> 
</xs:annotation> 
</xs:attribute> 
<xs:attribute name="StartYear" type="CreditCardYearType" use="optional"> 
<xs:annotation> 
    <xs:documentation>Switch card's start year.</xs:documentation> 
</xs:annotation> 
</xs:attribute> 

回答

3

OK,忽略我上面的代碼示例(我會離開它,因爲它可能會幫助別人,雖然)。我只記得,你可以做到這一點使用XmlEnumAttribute

public enum LeadingZeroMonth 
{ 
    [XmlEnum("01")] 
    January, 

    ... 

    [XmlEnum("12")] 
    December 
} 

,然後改變你使用情況的枚舉:

public LeadingZeroMonth ValidFromMonth { get; set; } 

這實際上是一個非常好的方式,因爲你現在有一個月的枚舉(這實際上是你從一開始就應該做的)。

+0

這是極好的 - 但我想我還是會需要多年來一直使用LeadingZero類的方法。 – 2009-04-27 10:24:32

+1

也許吧。這就是我放棄它的原因。我認爲他們是不同的足以保證他們作爲單獨的答案(取決於你的需要)。我真的會看到讓他們將年份字段擴大到4位數字。我的意思是,來吧,他們已經使用了冗長的格式(XML),他們認爲他們通過僅使用兩位數字來節省多少時間?但我明白,如果你堅持使用他們的架構。無論哪種方式,這兩件事應該有助於完成你所需要的。 – 2009-04-27 12:52:37

3

這是一個很大的代碼,但它你想要做什麼。要點是您可以創建一個新類(本例中爲LeadingZero)並實施IXmlSerializable來控制您從XML流中讀取/寫入的方式。希望這有助於:

using System; 
    using System.IO; 
    using System.Xml.Serialization; 

namespace StackOverflow 
{ 
    [Serializable] 
    public class LeadingZero : IXmlSerializable 
    { 
     public int Value { get; set; } 

     public LeadingZero() 
     { 
      Value = 0; 
     } 

     public LeadingZero(int value) 
     { 
      this.Value = value; 
     } 

     public override string ToString() 
     { 
      return Value.ToString("00"); 
     } 

     #region IXmlSerializable Members 

     public System.Xml.Schema.XmlSchema GetSchema() 
     { 
      return null; 
     } 

     public void ReadXml(System.Xml.XmlReader reader) 
     { 
      string s = reader.ReadElementString(); 
      int i; 
      if (int.TryParse(s, out i)) 
      { 
       Value = i; 
      } 
     } 

     public void WriteXml(System.Xml.XmlWriter writer) 
     { 
      writer.WriteString(Value.ToString("00")); 
     } 

     #endregion 
    } 

    [Serializable] 
    public class Complex 
    { 
     public LeadingZero ValidFromMonth { get; set; } 
     public LeadingZero ValidFromYear { get; set; } 
     public LeadingZero ExpiresEndMonth { get; set; } 
     public LeadingZero ExpiresEndYear { get; set; } 
    } 

    class Program 
    { 
     static void Main() 
     { 
      var seven = new LeadingZero(7); 

      XmlSerializer xml = new XmlSerializer(typeof(LeadingZero)); 

      StringWriter writer; 

      writer = new StringWriter(); 
      xml.Serialize(writer, seven); 

      string s = writer.ToString(); 

      Console.WriteLine(seven); 
      Console.WriteLine(); 
      Console.WriteLine(s); 

      Console.WriteLine(); 
      var newSeven = xml.Deserialize(new StringReader(s)) as LeadingZero; 
      Console.WriteLine(newSeven ?? new LeadingZero(0)); 

      var complicated = new Complex() 
      { 
       ValidFromMonth = new LeadingZero(7), 
       ValidFromYear = new LeadingZero(2009), 
       ExpiresEndMonth = new LeadingZero(6), 
       ExpiresEndYear = new LeadingZero(2010) 
      }; 

      Console.WriteLine(); 
      writer = new StringWriter(); 

      xml = new XmlSerializer(typeof(Complex)); 
      xml.Serialize(writer, complicated); 
      s = writer.ToString(); 
      Console.WriteLine(s); 

      var newComplicated = xml.Deserialize(new StringReader(s)) as Complex; 
      if (newComplicated != null) 
      { 
       Console.WriteLine(); 
       Console.WriteLine("Woo hoo!"); 
      } 

      Console.ReadLine(); 
     } 
    } 
} 

這是我得到的輸出:

07 

<?xml version="1.0" encoding="utf-16"?> 
<LeadingZero>07</LeadingZero> 

07 

<?xml version="1.0" encoding="utf-16"?> 
<Complex xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:/ 
/www.w3.org/2001/XMLSchema"> 
    <ValidFromMonth>07</ValidFromMonth> 
    <ValidFromYear>2009</ValidFromYear> 
    <ExpiresEndMonth>06</ExpiresEndMonth> 
    <ExpiresEndYear>2010</ExpiresEndYear> 
</Complex> 

Woo hoo! 
1

這種需求通常來自不理解XML的公司。我會問:他們是否提供了描述前導零日格式的XML模式?如果是這樣,你可以發佈定義這一天的部分嗎?


編輯根據編輯

感謝張貼的模式。它證實了我所關心的另一件事。你的整數不是整數。請注意0​​。這些是字符串,而不是整數。

+0

我已經更新了我的答案 – 2009-04-27 09:30:41

1

Disadvatage使用XmlEnum將是它無法可空

我會推薦

[XmlIgnore] 
    private int? _startMonth; 

    /// <remarks/> 
    [XmlAttributeAttribute] 
    public string StartMonth 
    { 
     get { return _startMonth == null ? null : _startMonth.ToString().PadLeft(2, '0'); } 
     set { _startMonth = string.IsNullOrEmpty(value) ? (int?)null : int.Parse(value); } 
    } 

這將讓你做出屬性可空