使用XML序列化格式化元素/属性值

时间:2009-04-24 15:35:54

标签: .net xml xml-serialization string-formatting

我有一个代表信用卡详细信息的课程。为表示有效期和有效月份和年份,我使用了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>

4 个答案:

答案 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); }
    }

这将允许您使属性可以为空