我有一个代表信用卡详细信息的课程。为表示有效期和有效月份和年份,我使用了int
类型的四个属性:
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
属性,但这感觉就像是第二个 - 率解决方案。
修改 经过一番考虑后,我想知道这实际上是不可行的。序列化没有问题,但为了使反序列化工作,您需要取消格式化序列化字符串。在上面这个简单的例子中,这很容易,但我不确定它是否可以在更一般的情况下工作。
EDIT2: 定义两位数要求的XML Schema如下所示。
简单类型定义:
<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>
答案 0 :(得分: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 :(得分:3)
好的,忽略我之前的代码示例(我会把它留下来,因为它可能会帮助其他人)。我记得你可以使用XmlEnumAttribute:
来做到这一点public enum LeadingZeroMonth
{
[XmlEnum("01")]
January,
...
[XmlEnum("12")]
December
}
然后将您的使用情况更改为枚举:
public LeadingZeroMonth ValidFromMonth { get; set; }
这实际上是一种非常好的方式,因为你现在有一个月的枚举(这是你应该从头开始做的)。
答案 2 :(得分:1)
这种要求通常来自不了解XML的公司。我不会假设这是这种情况,我会问:他们是否为您提供描述领先零日格式的XML架构?如果是这样,你可以发布定义当天的部分吗?
基于编辑的编辑
感谢您发布架构。它证实了我关心的另一件事。你的整数不是整数。请注意<restriction base="xs:string"/>
。这些是字符串,而不是整数。
答案 3 :(得分:1)
使用XmlEnum的Disadvatage是它不能为空的
我会推荐
[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); }
}
这将允许您使属性可以为空