2009-04-24 279 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); } 
    } 

这将让你做出属性可空